The more I develop, the more I'm logging everything I code.
These can be logs for the backend, but also histories for the users. A simple way to create a history is to log all changes made to the database.
Doctrine gives us access to the method onFlush to get all data that will be updated, added, or deleted before flushing.
This is a guide to quickly create logs inside Doctrine methods.
Connect onFlush method
Nothing here is out of usual things, we connect on the method onFlush of Doctrine.
First, we will connect to the Doctrine event.
#config\services.yaml
App\EventListener\DatabaseOnFlushListener:
tags:
- # these are the options required to define the entity listener
name: 'doctrine.event_listener'
event: 'onFlush'Then, we create our class with our onFlush method.
# src\EventListener\DatabaseOnFlushListener.php
class DatabaseOnFlushListener
{
/**
* @param OnFlushEventArgs $eventArgs
*/
public function onFlush(OnFlushEventArgs $eventArgs): void
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
// We will add methods there
}
}UnitOfWork contains database context, meaning all modifications that will be applied to our database.
Observe entities changes
# src\EventListener\DatabaseOnFlushListener.php
class DatabaseOnFlushListener
{
/**
* @param OnFlushEventArgs $eventArgs
*/
public function onFlush(OnFlushEventArgs $eventArgs): void
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityUpdates() as $entity) {
}
//... Check the documentation to get others methods
}
}Check the documentation to read more about available methods on UnitOfWork :

We now have access to :
instanceofto filter on an entity typegetEntityChangeSetto compare old and new valuescomputeChangeSetto insert a new entityrecomputeSingleEntityChangeSetto update an existing entity (be really careful here)
Let's take an example: we want to create a log every time we update a user.
# src\EventListener\DatabaseOnFlushListener.php
class DatabaseOnFlushListener
{
/**
* @param OnFlushEventArgs $eventArgs
*/
public function onFlush(OnFlushEventArgs $eventArgs): void
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityUpdates() as $entity) {
if ($entity instanceof User) {
$uow->computeChangeSets();
// We check if our entity contains some changes
$changeSet = $uow->getEntityChangeSet($entity);
if ($changeSet) {
$log = new Log();
$em->persist($log);
$uow->computeChangeSet($em->getClassMetadata(get_class($log)), $log);
}
}
}
}
}We get changeSet to check if the entity contains changes, then we create a new object Log that we insert.
But changeSet contains more information. It allows checking on each field of the user entity.
We suppose that the class User has one field enabled, and we only want to log when the user is moving from enabled to disabled
changeSet contains an index enabled is an array with 2 values :
[0]contains the old value[1]contains the new value
if ($changeSet && isset($changeSet['enabled']) && $changeSet['enabled'][1] === false) {
$log = new Log();
$em->persist($log);
$uow->computeChangeSet($em->getClassMetadata(get_class($log)), $log);
}Summary
onFlush is quite an easy way to create logs on your entities since each time your entity is updated, it will call your function inside.
But, and this is a huge but!
Even if it's possible, I do not recommend modifying your entities inside the onFlush method.
Instead, update all your data in your service.
You should not add any logic inside onFlush, since as the name suggests, it is only used to flush your database.

Join the conversation.