New events doctrine 2.4
Simon Constans3 min read
Here is an introduction to events in Doctrine and the changes brought by Doctrine 2.4. You will find all the code in this article on the following github repository.
Events in doctrine
Doctrine launches many events during the lifecycle of an entity.
The most commonly used are:
- prePersist: this event is launched before persisting an entity
- postPersist: this event is launched after persisting an entity
- preUpdate: this event is launched before updating an entity
- postUpdate: this event is launched after updating an entity
- preRemove: this event is launched before deleting an entity
- postRemove: this event is launched after deleting an entity
In a doctrine entity
To use events in an entity you have to specify Lifecycle Callbacks with the help of annotations. We will take the example of an ant class:
/** * Ant * * @ORM\Table() * @ORM\HasLifecycleCallbacks */ class Ant {
To attach a listener to a Doctrine Event, you can add the @ORMPrePersist to the function in your entity class like this:
/** * @ORM\PrePersist */ public function setCreatedAtAndUpdatedAtPrePersist() { $now = new \Datetime();
$this->createdAt = $now;
$this->updatedAt = $now;
}
The changes in Doctrine 2.4 make it possible to access an $event variable of type LifecycleEventArgs or PreUpdateEventArgs. With it, you have access to the EntityManager and the UnitOfWork.
class Ant { … /** * @ORM\PostUpdate */ public function postUpdate(LifecycleEventArgs $event) { $entity = $event->getEntity(); $em = $event->getEntityManager();
...
}
/\*\*
\*
\* @ORM\\PreUpdate
\*/
public function casteRules(PreUpdateEventArgs $event)
{
$entity = $event->getEntity();
$em = $event->getEntityManager();
if ($event->hasChangedField('caste') == self::CASTE\_QUEEN) {
$this->caste = $event->getOldValue('caste');
}
}
}
Ok it’s nice and all but keep in mind that you absolutely want to separate the logic from your entity to have an easily readable code ;) That is why there are doctrine listeners.
Doctrine Listeners
In this section, I will show you how to use listeners in Doctrine 2.4.
Creating a listener is very easy. First, annotate an entity with:
@ORM\EntityListeners({“UserListener”})
You can specify multiple listeners like this:
@ORM\EntityListeners({“UserListener”, “UserListener2”})
But do not forget that if your listener is in another directory you must specify its namespace
@ORM\EntityListeners({“kosssi/UserBundle/Listener/UserListener”})
Secondly, create your own listener class in which you will be able to use all event functions like postPersist, preUpdate, postUpdate, etc. Here is a quick example of what your prePersist function could look like:
public function prePersist(Snake $snake, LifecycleEventArgs $event) { $now = new \Datetime();
$snake->setCreatedAt($now); $snake->setUpdatedAt($now); }
You should immediately see a problem: What if you want to access other services from the listener? Since it is Doctrine that instantiates your listener, you cannot add parameters to the constructor.
This is where the ResolverListener comes in. You can create only one ResolverListener by project. In this class, you will need to do the mapping between the listener name and the listener service that you have previously created in your services.yml with any parameters you want.
class ListenerResolver extends DefaultEntityListenerResolver { public function __construct($container) { $this->container = $container; }
public function resolve($className)
{
$id = null;
if ($className === 'kosssi\\ExampleDoctrineEventsBundle\\Listener\\SnakeSizeListener') {
$id = 'kosssi\_listener\_snake\_size';
}
if (is\_null($id)) {
return new $className();
} else {
return $this->container->get($id);
}
}
}
It’s easy to configure the listener resolver in config.yml with DoctrineBundle:
doctrine: orm: entity_listener_resolver: user_listener_resolver
In conclusion
So you see it is quite simple to create events in Doctrine. Events programming is really exciting since it allows you to interact with objects as they change and go through their lifecycles.