Skip to content

Instantly share code, notes, and snippets.

@johnkary
Created November 21, 2012 19:37
Show Gist options
  • Save johnkary/4127142 to your computer and use it in GitHub Desktop.
Save johnkary/4127142 to your computer and use it in GitHub Desktop.
Doctrine listener for audit log functionality
<?php
namespace Acme\StudentBundle\Listener;
use Doctrine\Common\EventSubscriber,
Doctrine\ORM\Events,
Doctrine\ORM\Event\OnFlushEventArgs,
Doctrine\ORM\EntityManager,
DoctrineExtensions\Versionable\Entity\ResourceVersion;
use Acme\StudentBundle\Entity\Student;
use Acme\StudentBundle\Entity\ActivityLog;
/**
* Listens for changes on Student entity and reacts accordingly when certain
* properties are changed.
*
* @author johnkary
*/
class StudentChangeSubscriber implements EventSubscriber
{
public function getSubscribedEvents()
{
return array(Events::onFlush);
}
/**
* @param OnFlushEventArgs $args
*/
public function onFlush(OnFlushEventArgs $args)
{
/* @var $em \Doctrine\ORM\EntityManager */
$em = $args->getEntityManager();
/* @var $uow \Doctrine\ORM\UnitOfWork */
$uow = $em->getUnitOfWork();
/* @var $resourceClass Doctrine\ORM\Mapping\ClassMetadata */
$resourceClass = $em->getClassMetadata('Acme\StudentBundle\Entity\ActivityLog');
foreach ($uow->getScheduledEntityUpdates() as $entity) {
if ($entity instanceof Student) {
$entityClass = $em->getClassMetadata(get_class($entity));
// Cannot use multi-column identifier
$studentId = $entityClass->getIdentifierValues($entity);
if (1 === count($studentId) && current($studentId)) {
$studentId = current($studentId);
} else {
throw new \RuntimeException('A single identifier column is required.');
}
$oldValues = array_map(function($changeSetField) {
return $changeSetField[0];
}, $uow->getEntityChangeSet($entity));
// Don't need old ID anymore
unset($oldValues[$entityClass->getSingleIdentifierFieldName()]);
// Has the status field been changed?
if ($this->hasChanged('status', $oldValues, $entity->getStatus())) {
$typeRepository = $em->getRepository('Acme\StudentBundle\Entity\ActivityLogType');
$statusChangeType = $typeRepository->findOneRootNodeByTitle('Status Change');
$manualType = $typeRepository->findOneChildByTitleWithDirectParent('Manual', $statusChangeType);
$message = sprintf('Status changed to "%s"', $entity->getStatus());
$activityLog = new ActivityLog();
// @TODO: Provide way to specify this or inject a subtype of ActivityLog to set below values on
$activityLog->setType($manualType);
$activityLog->setOccurredAt(new \DateTime('now'));
// $activityLog->setWho($currentUser);
$activityLog->setWho('Admin');
$activityLog->setDescription($message);
$em->persist($activityLog);
// Necessary instead of $em->flush() because we're already in flush process
$uow->computeChangeSet($resourceClass, $activityLog);
}
}
}
}
/**
* Checks if a given entity property has changed.
*
* @param string $propertyName Name of entity property (e.g. $this->status should use 'status')
* @param array $oldValues Old values before they were changed
* @param mixed $newValue New value on the entity
* @return boolean
*/
private function hasChanged($propertyName, array $oldValues, $newValue)
{
if (!array_key_exists($propertyName, $oldValues)) {
return false;
}
return $oldValues[$propertyName] != $newValue;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment