From a36a1624fb4deb94d9261abf7aa9bcecf7246fc1 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 13 Oct 2011 22:58:22 +0200 Subject: [PATCH] [DDC-1415] Add EntityEventDelegatee, allowing to restrict emitting events to certain entity classes only. --- .../ORM/Event/EntityEventDelegator.php | 110 ++++++++++++++++++ .../ORM/Event/EntityEventDelegateeTest.php | 91 +++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 lib/Doctrine/ORM/Event/EntityEventDelegator.php create mode 100644 tests/Doctrine/Tests/ORM/Event/EntityEventDelegateeTest.php diff --git a/lib/Doctrine/ORM/Event/EntityEventDelegator.php b/lib/Doctrine/ORM/Event/EntityEventDelegator.php new file mode 100644 index 000000000..d7c46e68e --- /dev/null +++ b/lib/Doctrine/ORM/Event/EntityEventDelegator.php @@ -0,0 +1,110 @@ +. +*/ + +namespace Doctrine\ORM\Event; + +use \Doctrine\Common\EventSubscriber; +use \LogicException; + +/** + * Delegate events only for certain entities they are registered for. + * + * @author Benjamin Eberlei + * @since 2.2 + */ +class EntityEventDelegator implements EventSubscriber +{ + /** + * Keeps track of all the event listeners. + * + * @var array + */ + private $listeners = array(); + + /** + * If frozen no new event listeners can be added. + * + * @var bool + */ + private $frozen = false; + + /** + * Adds an event listener that listens on the specified events. + * + * @param string|array $events The event(s) to listen on. + * @param string|array $entities The entities to trigger this listener for + * @param object $listener The listener object. + */ + public function addEventListener($events, $entities, $listener) + { + if ($this->frozen) { + throw new LogicException("Cannot add event listeners after EntityEventDelegator::getSubscribedEvents() " . + "is called once. This happens when you register the delegator with the event manager."); + } + + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $events as $event) { + // Overrides listener if a previous one was associated already + // Prevents duplicate listeners on same event (same instance only) + $this->listeners[$event][$hash] = array('listener' => $listener, 'entities' => array_flip((array)$entities)); + } + } + + /** + * Adds an EventSubscriber. The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + * + * @param Doctrine\Common\EventSubscriber $subscriber The subscriber. + */ + public function addEventSubscriber(EventSubscriber $subscriber, $entities) + { + $this->addEventListener($subscriber->getSubscribedEvents(), $entities, $subscriber); + } + + /** + * Returns an array of events this subscriber wants to listen to. + * + * @return array + */ + public function getSubscribedEvents() + { + $this->frozen = true; + return array_keys($this->listeners); + } + + /** + * Delegate the event to an appropriate listener + * + * @param $eventName + * @param $event + * @return void + */ + public function __call($eventName, $args) + { + $event = $args[0]; + foreach ($this->listeners[$eventName] AS $listenerData) { + $class = get_class($event->getEntity()); + if (isset($listenerData['entities'][$class])) { + $listenerData['listener']->$eventName($event); + } + } + } +} diff --git a/tests/Doctrine/Tests/ORM/Event/EntityEventDelegateeTest.php b/tests/Doctrine/Tests/ORM/Event/EntityEventDelegateeTest.php new file mode 100644 index 000000000..5c4db0101 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Event/EntityEventDelegateeTest.php @@ -0,0 +1,91 @@ +delegator = new \Doctrine\ORM\Event\EntityEventDelegator(); + } + + public function testGetSubscribedEventsWhenEmpty() + { + $this->assertEquals(array(), $this->delegator->getSubscribedEvents()); + } + + public function testAddListener() + { + $this->delegator->addEventListener('postLoad', 'stdClass', new DelegateeEventListener()); + $this->assertEquals(array('postLoad'), $this->delegator->getSubscribedEvents()); + } + + public function testAddSubscriber() + { + $this->delegator->addEventSubscriber(new DelegateeEventListener(), 'stdClass'); + $this->assertEquals(array('postLoad'), $this->delegator->getSubscribedEvents()); + } + + public function testAddListenerAfterFrozenThrowsException() + { + $this->delegator->getSubscribedEvents(); // freezes + + $this->setExpectedException("LogicException", "Cannot add event listeners aft"); + $this->delegator->addEventListener('postLoad', 'stdClass', new DelegateeEventListener()); + } + + public function testDelegateEvent() + { + $delegatee = new DelegateeEventListener(); + $this->delegator->addEventListener('postLoad', 'stdClass', $delegatee); + + $event = new \Doctrine\ORM\Event\LifecycleEventArgs(new \stdClass(), $this->_getTestEntityManager()); + $this->delegator->postLoad($event); + $this->delegator->postLoad($event); + + $this->assertEquals(2, $delegatee->postLoad); + } + + public function testDelegatePickEntity() + { + $delegatee = new DelegateeEventListener(); + $this->delegator->addEventListener('postLoad', 'stdClass', $delegatee); + + $event1 = new \Doctrine\ORM\Event\LifecycleEventArgs(new \stdClass(), $this->_getTestEntityManager()); + $event2 = new \Doctrine\ORM\Event\LifecycleEventArgs(new \Doctrine\Tests\Models\CMS\CmsUser(), $this->_getTestEntityManager()); + $this->delegator->postLoad($event1); + $this->delegator->postLoad($event2); + + $this->assertEquals(1, $delegatee->postLoad); + } +} + +class DelegateeEventListener implements \Doctrine\Common\EventSubscriber +{ + public $postLoad = 0; + + public function postLoad($args) + { + $this->postLoad++; + } + + /** + * Returns an array of events this subscriber wants to listen to. + * + * @return array + */ + function getSubscribedEvents() + { + return array('postLoad'); + } +} \ No newline at end of file