In this article I will show a simple way to log every change to doctrine 2 entity.
This one will not be copy / paste. Just check out the code and help yourself.
1. Create event service
/** * @developer Ivan Gospodinow */ namespace Application\Service; use Application\Entity\DbLogEntity; use Application\Entity\AbstractEntity; use Doctrine\Common\Persistence\Event\LifecycleEventArgs; use \DateTime; class DoctrineLogChangesService { /** * Hold the changes * @var type */ protected static $rows = []; protected $sm; protected $disabled = [ //'Application\Entity\UserEntity', ]; public function __construct($sm) { $this->sm = $sm; $sm->get(‘Application’) ->getEventManager() ->attach(‘finish ’, [$this, 'flush']); } public function flush() { $orm = $this->sm->get(‘doctrine.entitymanager.orm_default’); foreach (self::$rows as $row) { $orm->persist($row); } if (!empty(self::$rows)) { $orm->flush(); } } public function getUnitOfWork() { return $this->sm->get(‘doctrine.entitymanager.orm_default’)->getUnitOfWork(); } public function getEntityTableName(LifecycleEventArgs $args) { $meta = $args->getObjectManager()->getClassMetadata(get_class($args->getObject())); return $meta->table['name']; } public function getEntityFields(LifecycleEventArgs $args) { $meta = $args->getObjectManager()->getClassMetadata(get_class($args->getObject())); $fields = []; foreach ($meta->fieldMappings as $field) { $fields[$field['fieldName']] = $field['columnName']; } foreach ($meta->associationMappings as $field) { if (isset($field['sourceToTargetKeyColumns'])) { $fields[$field['fieldName']] = key($field['sourceToTargetKeyColumns']); } } return $fields; } /** * Adds all initial fields * @param LifecycleEventArgs $args * @return type */ public function postPersist(LifecycleEventArgs $args) { $entity = $args->getObject(); if (in_array(get_class($entity), $this->disabled)) { return; } $tableName = $this->getEntityTableName($args); $fields = $this->getEntityFields($args); foreach ($fields as $field => $column) { $value = call_user_func_array([$entity, 'get' . ucfirst($field)], []); $row = [ 'user' => $this->sm->get('User')->getId(), 'table' => $tableName, 'pk' => $entity->getId(), 'column' => $column, 'oldValue' => null, 'newValue' => $value instanceof AbstractEntity ? $value->getId() : $value ]; $log = new DbLogEntity(); $log->exchangeArray($row); self::$rows[] = $log; } } /** * Add only new changes * @param LifecycleEventArgs $args * @return type */ public function preUpdate(LifecycleEventArgs $args) { $entity = $args->getObject(); if (in_array(get_class($entity), $this->disabled) || !$entity->canUseLifecycleEvents()) { return; } $tableName = $this->getEntityTableName($args); $fields = $this->getEntityFields($args); $set = $this->getUnitOfWork()->getEntityChangeSet($entity); foreach ($set as $property => $changes) { $oldValue = $changes[0]; $newValue = $changes[1]; if ($changes[0] instanceof AbstractEntity || $changes[1] instanceof AbstractEntity) { if ($changes[0] instanceof AbstractEntity) { $oldValue = $changes[0]->getId(); } if ($changes[1] instanceof AbstractEntity) { $newValue = $changes[1]->getId(); } } if ($oldValue == $newValue) { continue; } $row = [ ‘user’ => $this->sm->get(‘User’)->getId(), ‘ table’ => $tableName, ‘ pk’ => $entity->getId(), ‘ column’ => $fields[$property], ‘ oldValue’ => $this->getStringValue($oldValue), ‘ newValue’ => $this->getStringValue($newValue) ]; $log = new DbLogEntity(); $log->exchangeArray($row); self::$rows[] = $log; } } public function getStringValue($value) { if (is_scalar($value)) { return $value; } if ($value instanceof DateTime) { return $value->format('Y-m-d H:i:s'); } elseif (is_array($value)) { return serialize($value); } return $value; } }
2. Add the event service to Doctrine 2
/** * @developer Ivan Gospodinow */ namespace Application; use Zend\Mvc\MvcEvent; class Module { public function onBootstrap(MvcEvent $e) { $application = $e->getApplication(); $sm = $application->getServiceManager(); $doctrineEntityManager = $sm->get('doctrine.entitymanager.orm_default'); $doctrineEventManager = $doctrineEntityManager->getEventManager(); $doctrineEventManager->addEventListener( [ \Doctrine\ORM\Events::postPersist, \Doctrine\ORM\Events::preUpdate, ], new Service\DoctrineLogChangesService($sm) ); } }
3. DbLogEntity
/** * @developer Ivan Gospodinow */ namespace Application\Entity; use Doctrine\ORM\Mapping as ORM; use \Exception; /** * @ORM\Table(name="__dblog") * @ORM\Entity() */ class DbLogEntity { /** * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") * @ORM\Column(type="integer", name="id") */ protected $id; /** * @ORM\Column(type="string", name="user_id") */ protected $user; /** * @ORM\Column(type="string", name="table_name") */ protected $table; /** * @ORM\Column(type="integer", name="pk") */ protected $pk; /** * @ORM\Column(type="string", name="column_name") */ protected $column; /** * @ORM\Column(type="string", name="old_value") */ protected $oldValue; /** * @ORM\Column(type="string", name="new_value") */ protected $newValue; /** * @ORM\Column(type="datetime", name="timestamp") */ protected $timestamp; }
Simple as that.
Suggestions or problems ? Write a comment.