Search code examples
symfonyevent-listenercircular-referencesymfony-2.7

Injecting EntityManager-dependend service into Listener


I am trying to inject one of my services into an EntityListener in order to call some application specific behaviour when an entity gets updated.

My Logger service, used to store events in a LogEntry entity in my database:

class Logger
{
    /**
     * @var EntityManager $manager The doctrine2 manager
     */
   protected $manager;
   //...
}

The listener:

class EntityListener
{
    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
         // ...
    }
}

And the service definitions in my service.yml:

listener:
    class: Namespace\EntityListener
    arguments: [@logger]
    tags:
        - { name: doctrine.event_listener, event: preUpdate }

logger:
    class: Namespace\Logger
    arguments: [@doctrine.orm.entity_manager]

Unfortunately it results in a ServiceCircularReferenceException:

Circular reference detected for service "doctrine.orm.default_entity_manager", path: "doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection -> listener -> logger".

The problem obviously is that I inject the doctrine into the my service while it is also automatically injected into my listener. How do I proceed? I found a very similar question but the accepted answer is to inject the container which is obviously not favourable.

Any suggestions on how to solve my issue would be appreciated.


Small side note: I would like to avoid a solution depending on lazy services if possible.


Solution

  • First of all I switched from an EventListener to an EventSubscriber. From the docs:

    Doctrine defines two types of objects that can listen to Doctrine events: listeners and subscribers. Both are very similar, but listeners are a bit more straightforward.

    It turns out one can access the ObjectManager via the passed $args-parameter like so:

    /** @var Doctrine\Common\Persistence\ObjectManager $manager */
    $manager = $args->getObjectManager();
    

    So either use it directly in the callback:

    public function postUpdate(LifecycleEventArgs $args)
    {
        $manager = $args->getObjectManager();
        // ...
    

    ...or set it to an object field:

    /** @var ObjectManager $manager */
    private $manager;
    
    public function postUpdate(LifecycleEventArgs $args)
    {
        $this->manager = $args->getObjectManager();
        // ...