Search code examples
phpsymfonydoctrine-ormsymfony-3.2

Saving the getEntityChangeSet() result in Doctrine EventListener


I need to create changelog in the API for user actions on entities.

For example:

User updates entity Licensor I need to catch the changes and save them in the database in different table.

The first part I was able to do with Doctrine Event Listener

class ChangelogEventListener
{
   public function preUpdate($obj, PreUpdateEventArgs $eventArgs)
   {
       if ($obj instanceof LoggableInterface) {
            dump($eventArgs->getEntityChangeSet());
       }
   }
}

And with marking entity event listeners

/**
 * @ORM\EntityListeners(value={"AppBundle\EventSubscriber\Changelogger\ChangelogEventListener"})
 */
class Licensor implements LoggableInterface

But I'm not sure if it's even possible and if it makes sense to access the ORM entity manager in a preUpdate event.

If it isn't then what's the proper way to do it?

I've tried with Symfony's EventListener instead of Doctrine's but then I don't have access to getEntityChangeSet().


Solution

  • Check out Doctrine events, and specifically the preUpdate event. This event is the most restrictive, but you do have access to all of the fields that have changed, and their old/new values. You can change the values here on the entity being updated, unless it's an associated entity.

    Check out this answer, which suggests using an event subscriber, and then persisting to a logging entity.

    There is also this blog post that uses the preUpdate event to save a bunch of changesets to the internal listener class, then postFlush it persists any entities that are being changed, and calls flush again. However, I would not recommend this, as the Doctrine documentation explicitly states:

    postFlush is called at the end of EntityManager#flush(). EntityManager#flush() can NOT be called safely inside its listeners.

    If you went the route of that blog post you'd be better off using the onFlush() event and then doing your computeChangeSets() call after your persist(), like the first answer I posted.

    You can find a similar example here: