Skip to content

Instantly share code, notes, and snippets.

@lavoiesl
Created August 29, 2012 17:51
Show Gist options
  • Save lavoiesl/3516195 to your computer and use it in GitHub Desktop.
Save lavoiesl/3516195 to your computer and use it in GitHub Desktop.
DoctrineEntityListener
<?php
namespace Acme\CommonBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class DoctrineEntityCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$tagged = $container->findTaggedServiceIds('acme.listener.doctrine_entity');
if (!$tagged) {
return;
}
$definition = $container->register('acme.listener.doctrine_entity', 'Acme\DemoBundle\Listener\DoctrineEntityListener');
$events = array();
foreach ($tagged as $id => $tags) {
foreach ($tags as $tag) {
if (empty($tag['event']) || empty($tag['class'])) {
throw new \Exception("Services tagged acme.listener.doctrine_entity must have an event and a class argument");
}
$method = empty($tag['method']) ? null : $tag['method'];
$definition->addMethodCall('register', array($tag['class'], $tag['event'], new Reference($id), $method));
$events[$tag['event']] = 1;
}
}
// TODO support more than one evm
$evm = $container->getDefinition('doctrine.dbal.default_connection.event_manager');
$evm->addMethodCall('addEventSubscriber', array(new Reference('acme.listener.doctrine_entity')));
}
}
<?php
namespace Acme\DemoBundle\Listener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\Common\EventSubscriber;
class DoctrineEntityListener implements EventSubscriber
{
/**
* $hooks[$class][$event][get_class($service)] = $method
*/
private $hooks = array();
/**
* Built once each for each entity class submitted to resolve all class inheritance
* $cache[$class][$event][get_class($service)] = $method;
*/
private $cache = array();
/**
* Map of service_id => service to reduce size of $hooks and $cache
*/
private $services = array();
/**
* Registered events
* Used to build the service and hook only relevant events
*/
private $events = array();
/**
* Supported events by Doctrine so we detect errors early
*/
private static $validEvents = array(
'preRemove',
'postRemove',
'prePersist',
'postPersist',
'preUpdate',
'postUpdate',
'postLoad',
);
public function register($class, $event, $service, $method = null)
{
// Index with service class to ensure it is called only once
if (!in_array($event, self::$validEvents)) {
throw new \InvalidArgumentException("$event is not a valid Doctrine LifecycleEvent");
}
if (null === $method) {
$method = $event;
}
$this->services[get_class($service)] = $service;
$this->hooks[$class][$event][get_class($service)] = $method;
$this->events[$event] = 1;
}
/**
* Called by Doctrine EVM
*/
public function getSubscribedEvents()
{
return array_keys($this->events);
}
private function trigger($event, LifecycleEventArgs $args)
{
$class = get_class($args->getEntity());
foreach ($this->getServices($class, $event) as $service_id => $method) {
$service = $this->services[$service_id];
$service->$method($args);
}
}
private function getServices($class, $event)
{
if (!isset($this->cache[$class])) {
$this->cache[$class] = array();
foreach ($this->hooks as $parent => $events) {
// Test inheritance
if (is_a($parent, $class, true)) {
foreach ($events as $event => $services) {
foreach ($services as $service => $method) {
$this->cache[$class][$event][$service] = $method;
}
}
}
}
}
if (isset($this->cache[$class][$event])) {
return $this->cache[$class][$event];
} else {
return array();
}
}
/**
* Handles calls by Doctrine EVM
*/
public function __call($event, $args)
{
if (isset($this->events[$event]) && isset($args[0]) && $args[0] instanceof LifecycleEventArgs) {
$this->trigger($event, $args[0]);
}
}
}
services:
acme.listener.doctrine.user:
class: Acme\DemoBundle\Listener\AclListener
tags:
- { name: acme.listener.doctrine_entity, class: Acme\DemoBundle\Entity\User, event: postPersist }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment