There was 2 questions here saying injecting the whole service container should solve this. But question ... see below (note difference between try 2 & 3) ...
Try 1
public function __construct(SecurityContext $securityContext) {
$this->securityContext = $securityContext);
}
Curcular Reference. Okay ...
Try 2
public function __construct(ContainerInterface $container) {
$this->securityContext = $container->get('security.context');
}
Circular Reference (Why?, I am injecting the container like in try 3 except I got the security context only)
Try 3
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
Works.
This happens because your security context depends on this listener, probably via the entity manager being injected into a user provider. The best solution is to inject the container into the listener and access the security context lazily.
I typically don't like injecting the entire container into a service, but make an exception with Doctrine listeners because they are eagerly loaded and should therefore be as lazy as possible.
As of Symfony 2.6 this issue should be fixed. A pull request has just been accepted into the master. Your problem is described in here. https://github.com/symfony/symfony/pull/11690
As of Symfony 2.6, you can inject the security.token_storage
into your listener. This service will contain the token as used by the SecurityContext
in <=2.5. In 3.0 this service will replace the SecurityContext::getToken()
altogether. You can see a basic change list here: http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements#deprecated-the-security-context-service
Example usage in 2.6:
Your configuration:
services:
my.listener:
class: EntityListener
arguments:
- "@security.token_storage"
tags:
- { name: doctrine.event_listener, event: prePersist }
Your Listener
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class EntityListener
{
private $token_storage;
public function __construct(TokenStorageInterface $token_storage)
{
$this->token_storage = $token_storage;
}
public function prePersist(LifeCycleEventArgs $args)
{
$entity = $args->getEntity();
$entity->setCreatedBy($this->token_storage->getToken()->getUsername());
}
}
The reason "2" fails and "3" does not is because in option 2 you are trying to access the security context immediately from the container when it is likely not populated yet.
As best I can tell, Symfony2 parses through the config and instantiates the service one after the other and then moves onto the handling the rest of the request.
This means you cannot necessarily access the various parts of the container because it may be loading them in a different order. So you have the memory pointer to the container, and store that, but then let the framework finish building the full container before you try to access parts of it. A notable exception to this is when you directly inject the service into another service, at which point the container is making sure it has that service loaded first.
You can see the effects of this by making two services. A and B. A is passed B, and B is passed A. Now you have a circular reference. If you instead passed the container into both A and B, you could not access A from B and B from A without a problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With