Created
November 21, 2012 19:37
-
-
Save johnkary/4127142 to your computer and use it in GitHub Desktop.
Doctrine listener for audit log functionality
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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