Symfony: LoggingTranslator vs Translator - php

I want to inject my translations string into a service, so I used this in the service definition:
arguments: [#doctrine.orm.entity_manager, #translator]
I used this in the constructor:
public function __construct(\Doctrine\ORM\EntityManager $entityManager, \Symfony\Component\Translation\Translator $translator)
But I get this error:
.... __construct() must be an instance of Symfony\Component\Translation\Translator, instance of Symfony\Component\Translation\LoggingTranslator given...
What is the difference between the two?

In according with the news announcement, from the version 2.6 the translator component is defined as service like translator.default.
So change your service definition:
arguments: [#doctrine.orm.entity_manager, #translator]
with
arguments: [#doctrine.orm.entity_manager, #translator.default]

Symfony 2.6 introduced missing translations logging and for this the "translator" service alias was replaced by some kind of proxy to the real translator class.
As said in the other (and currently accepted) answer, the real translator class is now on the "translator.default" service. But using this service instead of "translator" will disable this new Symfony feature, so you may want to avoid that.
To fix your issue and still have access to the new features, change the code of your constructor to accept any implementation of TranslatorInterface :
public function __construct(\Doctrine\ORM\EntityManager $entityManager, \Symfony\Component\Translation\TranslatorInterface $translator)

Related

Symfony 4 Dependency Injection

I've got class like this,
use Doctrine\ORM\EntityManagerInterface;
class LoginTools {
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
and in Controller
$logTool = new LoginTools();
Question
Does autowire should pass EntityManagerInterface to LoginTools automatically?
Because when I call LoginTools class without passing the argument I get error
Too few arguments to function App\Utils\LoginTools::__construct(), 0 passed exactly 1 expected
With Regards,
Wiktor.
As you said, LoginTools is a service. That means, that you mustn't create it in code, like you do, Symfony creates it for you and you have just to inject this service in controller instead of EntityManagerInterface.
The thing is that Symfony has DI container, it's purpose is creating services based on your configuration from config/services.yaml and then injecting them into other services' constructors/functions/properties. There's no magic, all this code, which creates your services and inject them into other services, is generated automatically and is saved into var/cache dir by Symfony, you can check it by yourself.

how to use a service inside another service in symfony 2.6

I have a service setup in symfony 2.6 by name ge_lib and it looks like below
ge_lib:
class: GE\RestBundle\Services\GELib
arguments: [#session, #doctrine.orm.entity_manager, #manage_ge_proc]
inside GELib.php I have a requirement to use a function from another service manage_ge_proc
manage_ge_proc:
class: GE\RestBundle\Services\GEManageProcedure
arguments: [#doctrine.orm.entity_manager, #manage_ge_native_query]
if I try to use it this way, it is not working
$emailInv = $this->get('manage_ge_proc');
$sendStatus = $emailInv->pSendGeneralEmail(.....);
It gives error saying that unable to find any get function by that name. generally this -> $this->get('manage_ge_proc');works in any controller.But how do i use it in service?.
I tried $this->getContainer()->get('manage_ge_proc'); but it did not work.
This call is fetching service from DI container, which you dont have in your service
$this->get('manage_ge_proc');
It works in controller because DI container is automatically injected there.
Since you have this line in you services.yml, which tells Symfony to inject #manage_de_proc service into ge_lib constructor
arguments: [#session, #doctrine.orm.entity_manager, #manage_ge_proc]
you should be able to pick #manage_ge_proc from constructor like this:
public function __construct(
Session $session,
EntityManager $entityManager,
GEManageProcedure $manageGeProc
)
{
//... whatever you do in your constructor
$this->manageGeProc = $manageGeProc;
}

Symfony2 access private services in tests

Currently I'm working on testing some services in Symfony2 and I'm trying to use Guzzle MockPlugin for controlling CURL responses. Symfony version 2.3.8 is used. I've got to an interesting behaviour and I'm not sure if this is a Symfony2 bug or not.
I have these services in services.yml:
lookup_service_client:
class: FOO
public: false
factory_service: lookup_client_builder
factory_method: build
lookup_repository_auth_type:
class: AuthType
arguments: ["#lookup_service_client"]
lookup_repository_cancel_reason:
class: CancelReason
arguments: ["#lookup_service_client"]
payment_service_client:
class: FOO
public: false
factory_service: payment_client_builder
factory_method: build
payment_repository:
class: Payment
arguments: ["#payment_service_client"]
The name of the classes are not important. You can see that both "lookup_service_client" and "lookup_service_client" are PRIVATE services.
I have a test class, which extends Symfony\Bundle\FrameworkBundle\Test\WebTestCase. In one test I need to do something like:
$lookup = $this->client->getContainer()->get('lookup_service_client');
$payment = $this->client->getContainer()->get('payment_service_client');
I expected that, setting those services as PRIVATE, will not let me retrieve the services from container in tests, but the actual result is:
$lookup = $this->client->getContainer()->get('lookup_service_client'); => returns the service instance
$payment = $this->client->getContainer()->get('payment_service_client'); => returns an exception saying: "You have requested a non-existent service"
The only difference between those tow service_client services is that "lookup_service_client" is injected in several other services, while "payment_service_client" is injected in only one other service.
So, the questions are:
Why I can retrieve from container "lookup_service_client", since I've set it to private?
Why I can retrieve "lookup_service_client", but cannot retrieve "payment_service_client" since the only difference is presented above?
Is it a Symfony2 bug that I can access private service?
There were some new changes regarding this in Symfony 4.1:
In Symfony 4.1, we did the same and now tests allow fetching private services by default.
In practice, tests based on WebTestCase and KernelTestCase now access to a special container via $client->getContainer() or the static::$container property that allows to fetch non-removed private services.
You can read more about it in the news post.
While this is not a bug, it is definitely counter intuitive. The manual specifically says:
Now that the service is private, you should not fetch the service
directly from the container:
$container->get('foo');
This may or may not work, depending on how the container has optimized
the service instanciation and, even in the cases where it works, is
deprecated. Simply said: A service can be marked as private if you do
not want to access it directly from your code.
Which is why the core team has decided to make this behavior more consistent and intuitive in Symfony 4:
Setting or unsetting a private service with the Container::set() method is deprecated in Symfony 3.2 and no longer supported in 4.0;
Checking the existence of a private service with the Container::has() will always return false in Symfony 4.0;
Requesting a private service with the Container::get() method is deprecated in Symfony 3.2 and no longer returns the service in 4.0.
2018+ and Symfony 3.4/4.0+ solution
This approach with all its pros/cons is described in this post with code examples.
The best solution to access private services is to add a Compiler Pass that makes all services public for tests. That's it. How does it look in practice?
1. Update Kernel
use Symfony\Component\HttpKernel\Kernel;
+use Symplify\PackageBuilder\DependencyInjection\CompilerPass\PublicForTestsCompilerPass;
final class AppKernel extends Kernel
{
protected function build(ContainerBuilder $containerBuilder): void
{
$containerBuilder->addCompilerPass('...');
+ $containerBuilder->addCompilerPass(new PublicForTestsCompilerPass());
}
}
2. Require or create own Compiler Pass
Where PublicForTestsCompilerPass looks like:
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
final class PublicForTestsCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $containerBuilder): void
{
if (! $this->isPHPUnit()) {
return;
}
foreach ($containerBuilder->getDefinitions() as $definition) {
$definition->setPublic(true);
}
foreach ($containerBuilder->getAliases() as $definition) {
$definition->setPublic(true);
}
}
private function isPHPUnit(): bool
{
// defined by PHPUnit
return defined('PHPUNIT_COMPOSER_INSTALL') || defined('__PHPUNIT_PHAR__');
}
}
To use this class, just add the package by:
composer require symplify/package-builder
But of course, the better way is to use own class, that meets your needs (you might Behat for tests etc.).
Then all your tests will keep working as expected!
Let me know, how that works for you.
Check them in the container:
container:debug lookup_service_client
container:debug payment_service_client
in your example they both have class "FOO", maybe that's the case

symfony2 SoapServer EntityManager injection in service

I've got a problem with the native php SoapServer in symfony2.
The SoapServer uses a "UserService" Service-Class in symfony2, which should be instantiated with the symfony2 EntityManager injected so that I can get access to my "UserRepository".
To clarify:
Soap-Server:
$oSOAPServer = new \SoapServer('/path/to/wsdl');
$oSOAPServer->setClass("my\Namespace\UserService");
$oSOAPServer->handle();
Service:
use \Doctrine\ORM\EntityManager as EntityManager;
class UserService
{
protected $em;
public function __construct(EntityManager $em) {
$this->em = $em;
}
...
Problem is: the SoapServer always returns an Internal Server Error.
The service itself works called in symfony2 directly.
Is it even possible to inject the EntityManager when called/instantiated by the SoapServer?
Thanks in advance!
Martin
Instead of using SoapServer::setClass() you could use SoapServer::setObject() and pass your service in:
$oSOAPServer = new \SoapServer('/path/to/wsdl');
$oSOAPServer->setObject($container->get('my_service'));
$oSOAPServer->handle();
Implementing a soap server is documented in the Symfony documentation: How to Create a SOAP Web Service in a Symfony2 Controller
Also, if you only need a repository, don't inject the whole entity manager. Register your repository as a service and inject it to your service.

Symfony2 global functions

For example i have algorithmic function, which calculates specific hash-code. Function itself is 300+ lines of code. I need to use that functions many times in many different controllers in my bundle. Where can i store my calculate_hash() to use it in my bundle ? Can i access it from other bundles ?
Can i also write global calculate_hash() which have access to entity manager ?
Didn't find my answer here.
In the Symfony2 world, this is clearly belonging to a service. Services are in fact normal classes that are tied to the dependency injection container. You can inject them the dependencies you need. For example, say your class where the function calculate_hash is located is AlgorithmicHelper. The service holds "global" functions. You define your class something like this:
namespace Acme\AcmeBundle\Helper;
// Correct use statements here ...
class AlgorithmicHelper {
private $entityManager;
public function __construct(EntityManager $entityManager) {
$this->entityManager = $entityManager;
}
public function calculate_hash() {
// Do what you need, $this->entityManager holds a reference to your entity manager
}
}
This class then needs to be made aware to symfony dependecy container. For this, you define you service in the app/config/config.yml files by adding a service section like this:
services:
acme.helper.algorithmic:
class: Acme\AcmeBundle\Helper\AlgorithmicHelper
arguments:
entityManager: "#doctrine.orm.entity_manager"
Just below the service, is the service id. It is used to retrieve your service in the controllers for example. After, you specify the class of the service and then, the arguments to pass to the constructor of the class. The # notation means pass a reference to the service with id doctrine.orm.entity_manager.
Then, in your controller, you do something like this to retrieve the service and used it:
$helper = $this->get('acme.helper.algorithmic');
$helper-> calculate_hash();
Note that the result of the call to $this->get('acme.helper.algorithmic') will always return the same instance of the helper. This means that, by default, service are unique. It is like having a singleton class.
For further details, I invite you to read the Symfony2 book. Check those links also
The service container section from Symfony2 book.
An answer I gave on accesing service outside controllers, here.
Hope it helps.
Regards,
Matt
Braian in comment asked for Symfony 3 answer, so here is one Symfony 3.3 (released May 2017):
1. The original class remains the same
namespace Acme\AcmeBundle\Helper;
use Doctrine\ORM\EntityManager;
final class AlgorithmicHelper
{
/**
* #var EntityManager
*/
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function calculateHash()
{
// Do what you need, $this->entityManager holds a reference to your entity manager
}
}
2. Service registration is much simpler
# app/config/services.yml
services:
_defaults: autowire # this enabled constructor autowiring for all registered services
Acme\AcmeBundle\Helper\AlgorithmicHelper: ~
3. Use constructor injection to get the service
use Acme\AcmeBundle\Helper\AlgorithmicHelper;
class SomeController
{
/**
* #var AlgorithmicHelper
*/
private $algorithmicHelper;
public function __construct(AlgorithmicHelper $algorithmicHelper)
{
$this->algorithmicHelper = $algorithmicHelper;
}
public function someAction()
{
// some code
$hash = $this->algorithmicHelper->calculateHash();
// some code
}
}
You can read about Symfony 3.3 dependency injection (in this case registering services in config and using it in controller) news in these 2 posts:
https://www.tomasvotruba.cz/blog/2017/05/07/how-to-refactor-to-new-dependency-injection-features-in-symfony-3-3/
https://symfony.com/blog/the-new-symfony-3-3-service-configuration-changes-explained

Categories