When working on a little more complex application is inevitable that we’ll get to the point when we need to trigger an action when something happens somewhere in our application. Quite often is the case that we need to bind our actions to an entity, like for example notify all subscribers to a blog post that the post they are following has changed.

While this task can be achieved in various ways (custom event listener, doctrine listeners/subscribers), there is a more suitable way which provides the best performance when compared to doctrine listeners (as those will be triggered on every entity action), and much more easier to implement and maintain than a custom event with custom listener, and that is with Doctrine Entity Listener.

This type of listener will be invoked just to the specified entity. The best part of this is that we can register this listeners as services which allows us to inject whatever we want in them and build some really complex logic.

And to use it is very easy, just make sure you have Doctrine ORM 2.4 and DoctrineBundle 1.3.

This is how the service definition should look like in service.yml:

#services.yml
services:
    user_listener:
        class: AcmeDemoBundleEntityListenerUserListener
        arguments: [@service_container]
        tags:            
            - { name: doctrine.orm.entity_listener}

And the listener itself:

<?php
namespace AcmeDemoBundleEntityListener;

use DoctrineORMEventLifecycleEventArgs;
use AcmeDemoBundleEntityUser;
use SymfonyComponentDependencyInjectionContainer;
use DoctrineORMMapping as ORM;

class UserListener
{
    
    protected $container;
    
    public function __construct(Container $container)
    {
        $this->container = $container;
    }
    
    /** @ORMPrePersist */
    public function prePersistHandler(User $user, LifecycleEventArgs $event)
    {       
        // Implement your custom logic
    }
}

If you get an exception for ServiceCircularReferenceException: Circular reference detected for service ... when you inject a certain service into your listener, then simply inject the whole container. There is a known issue, and the recommended way is to inject the whole container in this case, as this will lazy-load the service you need.

 

The documentation is straight forward and doesn’t need any more details. Just follow it on Symfony’s website.

 

Have fun coding :)