Nomisoft
Menu

Symfony & Doctrine event listeners

17th August 2016

Following on from my previous articles on Symfony event listeners we can also use event listeners with Doctrine. This enables us to run code whenever Doctrine entities are created, saved or deleted.

In this particular example we're going to be hooking into the event that is fired when a new entity is first created and persisted to our database. Say we have a registration form on our site and we want to send an email to the user upon registering. We might put this code in our controller, however a potentially better solution would be to create a listener that is fired on the postPersist event. This helps us to keep our controller cleaner and helps code re-use in the case that Users are created in multiple places/controllers across our application.


#src/AppBundle/EventListener/RegistrationListener
namespace AppBundle\EventListener;

use AppBundle\Entity\User;
use Doctrine\ORM\Event\LifecycleEventArgs;

class RegistrationListener
{
    protected $mailer;
    protected $templating;

    public function __construct($mailer, $templating)
    {
        $this->mailer     = $mailer;
        $this->templating = $templating;
    }

    public function postPersist(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        if ($entity instanceof User) {

            $message = \Swift_Message::newInstance()
                                     ->setSubject('Welcome')
                                     ->setFrom('registrations@example.com')
                                     ->setTo($entity->getEmail())
                                     ->setBody(
                                         $this->templating->render(
                                             ':email/registration.html.twig'
                                         ),
                                         'text/html'
                                     );

            $this->mailer->send($message);
        }
    }

}

We want access to the mailer service and also the twig templating service so both of these dependencies are injected into our class.

We can get our entity from calling the getEntity() function on the LifecycleEventArgs object that is passed to our listener $args->getEntity();. Because our listener listens to all postPerist events across all entities we have to ensure our code only runs if the object being persisted is a User entity, i.e we don't want to run this if it's a Product being persistd or a BlogPost if ($entity instanceof User) {}

As always we need to register our listener in our app/config/services.yml file. We tag it so doctrine registers our listener/


services:
    # new user email notification listener
    app.doctrine.new_user_listener:
        class: AppBundle\EventListener\RegistrationListener
        arguments: ['@mailer', '@templating']
        tags:
            - { name: doctrine.event_listener, event: postPersist }

Now whenever we create a new User and persist it our registration email is sent. Note that any subsequent changes to our user which are persisted and flushed do NOT fire the postPersist event, they will call the postUpdate event instead so our code is only fired the very first time our user is created.


$user = new User();
$manager->persist($user);
$manager->flush();

The above example uses the postPerist event so that our code is fired after the database INSERT operation is called. You may want to run some code before the INSERT operation in which case you'd use the prePersist event. You might also want to perform an action when a User is updated or deleted. There's a full list of the different events you can listen to on the Doctrine events reference.