From 315f7ba43b9c2226c9d801142ade87c4f5a8f56a Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Mon, 30 Jul 2012 22:26:55 -0300 Subject: [PATCH] call listeners in UoW --- lib/Doctrine/ORM/UnitOfWork.php | 131 ++++++++--- .../Models/Company/ContractSubscriber.php | 62 ++++- .../EntityListenersDispatcherTest.php | 215 +++++++++++++++++- .../ORM/Mapping/AbstractMappingDriverTest.php | 8 +- 4 files changed, 378 insertions(+), 38 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 9f54463c1..08c1af332 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -22,15 +22,21 @@ namespace Doctrine\ORM; use Exception; use InvalidArgumentException; use UnexpectedValueException; + use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\Common\NotifyPropertyChanged; use Doctrine\Common\PropertyChangedListener; use Doctrine\Common\Persistence\ObjectManagerAware; -use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Proxy\Proxy; +use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\ORM\Event\PreUpdateEventArgs; +use Doctrine\ORM\Event\PreFlushEventArgs; +use Doctrine\ORM\Event\OnFlushEventArgs; +use Doctrine\ORM\Event\PostFlushEventArgs; + /** * The UnitOfWork is responsible for tracking changes to objects during an * "object-level" transaction and for writing out changes to the database @@ -267,7 +273,7 @@ class UnitOfWork implements PropertyChangedListener { // Raise preFlush if ($this->evm->hasListeners(Events::preFlush)) { - $this->evm->dispatchEvent(Events::preFlush, new Event\PreFlushEventArgs($this->em)); + $this->evm->dispatchEvent(Events::preFlush, new PreFlushEventArgs($this->em)); } // Compute changes done since last commit. @@ -511,6 +517,11 @@ class UnitOfWork implements PropertyChangedListener $class->invokeLifecycleCallbacks(Events::preFlush, $entity); } + // Fire PreFlush entity listeners + if (isset($class->entityListeners[Events::preFlush])) { + $class->dispatchEntityListeners(Events::preFlush, $entity, new PreFlushEventArgs($this->em)); + } + $actualData = array(); foreach ($class->reflFields as $name => $refProp) { @@ -798,21 +809,32 @@ class UnitOfWork implements PropertyChangedListener } /** - * @param ClassMetadata $class + * @param \Doctrine\ORM\Mapping\ClassMetadata $class * @param object $entity * * @return void */ private function persistNew($class, $entity) { - $oid = spl_object_hash($entity); + $oid = spl_object_hash($entity); + $hasListeners = $this->evm->hasListeners(Events::prePersist); + $hasEntityListeners = isset($class->entityListeners[Events::prePersist]); + $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::prePersist]); - if (isset($class->lifecycleCallbacks[Events::prePersist])) { + if ($hasListeners || $hasEntityListeners) { + $event = new LifecycleEventArgs($entity, $this->em); + } + + if ($hasLifecycleCallbacks) { $class->invokeLifecycleCallbacks(Events::prePersist, $entity); } - if ($this->evm->hasListeners(Events::prePersist)) { - $this->evm->dispatchEvent(Events::prePersist, new LifecycleEventArgs($entity, $this->em)); + if ($hasEntityListeners) { + $class->dispatchEntityListeners(Events::prePersist, $entity, $event); + } + + if ($hasListeners) { + $this->evm->dispatchEvent(Events::prePersist, $event); } $idGen = $class->idGenerator; @@ -913,6 +935,7 @@ class UnitOfWork implements PropertyChangedListener $entities = array(); $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postPersist]); + $hasEntityListeners = isset($class->entityListeners[Events::postPersist]); $hasListeners = $this->evm->hasListeners(Events::postPersist); foreach ($this->entityInsertions as $oid => $entity) { @@ -924,7 +947,7 @@ class UnitOfWork implements PropertyChangedListener unset($this->entityInsertions[$oid]); - if ($hasLifecycleCallbacks || $hasListeners) { + if ($hasLifecycleCallbacks || $hasEntityListeners || $hasListeners) { $entities[] = $entity; } } @@ -946,14 +969,23 @@ class UnitOfWork implements PropertyChangedListener $this->addToIdentityMap($entity); } } - + foreach ($entities as $entity) { + + if ($hasListeners || $hasEntityListeners) { + $event = new LifecycleEventArgs($entity, $this->em); + } + if ($hasLifecycleCallbacks) { $class->invokeLifecycleCallbacks(Events::postPersist, $entity); } + if ($hasEntityListeners) { + $class->dispatchEntityListeners(Events::postPersist, $entity, $event); + } + if ($hasListeners) { - $this->evm->dispatchEvent(Events::postPersist, new LifecycleEventArgs($entity, $this->em)); + $this->evm->dispatchEvent(Events::postPersist, $event); } } } @@ -971,9 +1003,11 @@ class UnitOfWork implements PropertyChangedListener $persister = $this->getEntityPersister($className); $hasPreUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::preUpdate]); + $hasPreUpdateEntityListeners = isset($class->entityListeners[Events::preUpdate]); $hasPreUpdateListeners = $this->evm->hasListeners(Events::preUpdate); $hasPostUpdateLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postUpdate]); + $hasPostUpdateEntityListeners = isset($class->entityListeners[Events::postUpdate]); $hasPostUpdateListeners = $this->evm->hasListeners(Events::postUpdate); foreach ($this->entityUpdates as $oid => $entity) { @@ -981,17 +1015,26 @@ class UnitOfWork implements PropertyChangedListener continue; } + if ($hasPreUpdateListeners || $hasPreUpdateEntityListeners) { + $preEvent = new PreUpdateEventArgs($entity, $this->em, $this->entityChangeSets[$oid]); + } + + if ($hasPostUpdateListeners || $hasPostUpdateEntityListeners) { + $postEvent = new LifecycleEventArgs($entity, $this->em); + } + if ($hasPreUpdateLifecycleCallbacks) { $class->invokeLifecycleCallbacks(Events::preUpdate, $entity); $this->recomputeSingleEntityChangeSet($class, $entity); } + if ($hasPreUpdateEntityListeners) { + $class->dispatchEntityListeners(Events::preUpdate, $entity, $preEvent); + } + if ($hasPreUpdateListeners) { - $this->evm->dispatchEvent( - Events::preUpdate, - new Event\PreUpdateEventArgs($entity, $this->em, $this->entityChangeSets[$oid]) - ); + $this->evm->dispatchEvent(Events::preUpdate, $preEvent); } if (!empty($this->entityChangeSets[$oid])) { @@ -1004,8 +1047,12 @@ class UnitOfWork implements PropertyChangedListener $class->invokeLifecycleCallbacks(Events::postUpdate, $entity); } + if ($hasPostUpdateEntityListeners) { + $class->dispatchEntityListeners(Events::postUpdate, $entity, $postEvent); + } + if ($hasPostUpdateListeners) { - $this->evm->dispatchEvent(Events::postUpdate, new LifecycleEventArgs($entity, $this->em)); + $this->evm->dispatchEvent(Events::postUpdate, $postEvent); } } } @@ -1022,8 +1069,9 @@ class UnitOfWork implements PropertyChangedListener $className = $class->name; $persister = $this->getEntityPersister($className); - $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postRemove]); - $hasListeners = $this->evm->hasListeners(Events::postRemove); + $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postRemove]); + $hasEntityListeners = isset($class->entityListeners[Events::postRemove]); + $hasListeners = $this->evm->hasListeners(Events::postRemove); foreach ($this->entityDeletions as $oid => $entity) { if ($this->em->getClassMetadata(get_class($entity))->name !== $className) { @@ -1046,12 +1094,20 @@ class UnitOfWork implements PropertyChangedListener $class->reflFields[$class->identifier[0]]->setValue($entity, null); } + if ($hasListeners || $hasEntityListeners) { + $event = new LifecycleEventArgs($entity, $this->em); + } + if ($hasLifecycleCallbacks) { $class->invokeLifecycleCallbacks(Events::postRemove, $entity); } + if ($hasEntityListeners) { + $class->dispatchEntityListeners(Events::postRemove, $entity, $event); + } + if ($hasListeners) { - $this->evm->dispatchEvent(Events::postRemove, new LifecycleEventArgs($entity, $this->em)); + $this->evm->dispatchEvent(Events::postRemove, $event); } } } @@ -1691,12 +1747,24 @@ class UnitOfWork implements PropertyChangedListener break; case self::STATE_MANAGED: - if (isset($class->lifecycleCallbacks[Events::preRemove])) { + $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::preRemove]); + $hasEntityListeners = isset($class->entityListeners[Events::preRemove]); + $hasListeners = $this->evm->hasListeners(Events::preRemove); + + if ($hasListeners || $hasEntityListeners) { + $event = new LifecycleEventArgs($entity, $this->em); + } + + if ($hasLifecycleCallbacks) { $class->invokeLifecycleCallbacks(Events::preRemove, $entity); } - if ($this->evm->hasListeners(Events::preRemove)) { - $this->evm->dispatchEvent(Events::preRemove, new LifecycleEventArgs($entity, $this->em)); + if ($hasEntityListeners) { + $class->dispatchEntityListeners(Events::preRemove, $entity, $event); + } + + if ($hasListeners) { + $this->evm->dispatchEvent(Events::preRemove, $event); } $this->scheduleForDelete($entity); @@ -2695,13 +2763,24 @@ class UnitOfWork implements PropertyChangedListener } if ($overrideLocalValues) { - if (isset($class->lifecycleCallbacks[Events::postLoad])) { + $hasLifecycleCallbacks = isset($class->lifecycleCallbacks[Events::postLoad]); + $hasEntityListeners = isset($class->entityListeners[Events::postLoad]); + $hasListeners = $this->evm->hasListeners(Events::postLoad); + + if ($hasListeners || $hasEntityListeners) { + $event = new LifecycleEventArgs($entity, $this->em); + } + + if ($hasLifecycleCallbacks) { $class->invokeLifecycleCallbacks(Events::postLoad, $entity); } + if ($hasEntityListeners) { + $class->dispatchEntityListeners(Events::postLoad, $entity, $event); + } - if ($this->evm->hasListeners(Events::postLoad)) { - $this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($entity, $this->em)); + if ($hasListeners) { + $this->evm->dispatchEvent(Events::postLoad, $event); } } @@ -3177,14 +3256,14 @@ class UnitOfWork implements PropertyChangedListener private function dispatchOnFlushEvent() { if ($this->evm->hasListeners(Events::onFlush)) { - $this->evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->em)); + $this->evm->dispatchEvent(Events::onFlush, new OnFlushEventArgs($this->em)); } } private function dispatchPostFlushEvent() { if ($this->evm->hasListeners(Events::postFlush)) { - $this->evm->dispatchEvent(Events::postFlush, new Event\PostFlushEventArgs($this->em)); + $this->evm->dispatchEvent(Events::postFlush, new PostFlushEventArgs($this->em)); } } } diff --git a/tests/Doctrine/Tests/Models/Company/ContractSubscriber.php b/tests/Doctrine/Tests/Models/Company/ContractSubscriber.php index afc30933e..64e953a34 100644 --- a/tests/Doctrine/Tests/Models/Company/ContractSubscriber.php +++ b/tests/Doctrine/Tests/Models/Company/ContractSubscriber.php @@ -4,8 +4,19 @@ namespace Doctrine\Tests\Models\Company; class ContractSubscriber { - static public $prePersistCalls; static public $postPersistCalls; + static public $prePersistCalls; + + static public $postUpdateCalls; + static public $preUpdateCalls; + + static public $postRemoveCalls; + static public $preRemoveCalls; + + static public $preFlushCalls; + + static public $postLoadCalls; + static public $instances; public function __construct() @@ -28,4 +39,53 @@ class ContractSubscriber { self::$prePersistCalls[] = func_get_args(); } + + /** + * @PostUpdate + */ + public function postUpdateHandler(CompanyContract $contract) + { + self::$postUpdateCalls[] = func_get_args(); + } + + /** + * @PreUpdate + */ + public function preUpdateHandler(CompanyContract $contract) + { + self::$preUpdateCalls[] = func_get_args(); + } + + /** + * @PostRemove + */ + public function postRemoveHandler(CompanyContract $contract) + { + self::$postRemoveCalls[] = func_get_args(); + } + + /** + * @PreRemove + */ + public function preRemoveHandler(CompanyContract $contract) + { + self::$preRemoveCalls[] = func_get_args(); + } + + /** + * @PreFlush + */ + public function preFlushHandler(CompanyContract $contract) + { + self::$preFlushCalls[] = func_get_args(); + } + + /** + * @PostLoad + */ + public function postLoadHandler(CompanyContract $contract) + { + self::$postLoadCalls[] = func_get_args(); + } + } diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityListenersDispatcherTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityListenersDispatcherTest.php index c45bd944c..b60646612 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityListenersDispatcherTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityListenersDispatcherTest.php @@ -2,22 +2,223 @@ namespace Doctrine\Tests\ORM\Functional; -use Doctrine\ORM\Events; -use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\Tests\Models\Company\CompanyFixContract; -use Doctrine\Tests\Models\Company\CompanyFlexContract; use Doctrine\Tests\Models\Company\ContractSubscriber; require_once __DIR__ . '/../../TestInit.php'; +/** +* @group DDC-1955 +*/ class EntityListenersDispatcherTest extends \Doctrine\Tests\OrmFunctionalTestCase { - /** - * @group DDC-1955 - */ - public function testEntityListeners() + protected function setUp() + { + $this->useModelSet('company'); + parent::setUp(); + } + + public function testPreFlushListeners() + { + $fix = new CompanyFixContract(); + $fix->setFixPrice(2000); + + ContractSubscriber::$preFlushCalls = array(); + + $this->_em->persist($fix); + $this->_em->flush(); + + $this->assertCount(1,ContractSubscriber::$instances); + $this->assertCount(1,ContractSubscriber::$preFlushCalls); + + $this->assertSame($fix, ContractSubscriber::$preFlushCalls[0][0]); + + $this->assertInstanceOf( + 'Doctrine\Tests\Models\Company\CompanyFixContract', + ContractSubscriber::$preFlushCalls[0][0] + ); + + $this->assertInstanceOf( + 'Doctrine\ORM\Event\PreFlushEventArgs', + ContractSubscriber::$preFlushCalls[0][1] + ); + } + + public function testPostLoadListeners() { $this->markTestIncomplete(); } + + public function testPrePersistListeners() + { + $fix = new CompanyFixContract(); + $fix->setFixPrice(2000); + + ContractSubscriber::$prePersistCalls = array(); + + $this->_em->persist($fix); + $this->_em->flush(); + + $this->assertCount(1,ContractSubscriber::$instances); + $this->assertCount(1,ContractSubscriber::$prePersistCalls); + + $this->assertSame($fix, ContractSubscriber::$prePersistCalls[0][0]); + + $this->assertInstanceOf( + 'Doctrine\Tests\Models\Company\CompanyFixContract', + ContractSubscriber::$prePersistCalls[0][0] + ); + + $this->assertInstanceOf( + 'Doctrine\ORM\Event\LifecycleEventArgs', + ContractSubscriber::$prePersistCalls[0][1] + ); + } + + public function testPostPersistListeners() + { + $fix = new CompanyFixContract(); + $fix->setFixPrice(2000); + + ContractSubscriber::$postPersistCalls = array(); + + $this->_em->persist($fix); + $this->_em->flush(); + + $this->assertCount(1,ContractSubscriber::$instances); + $this->assertCount(1,ContractSubscriber::$postPersistCalls); + + $this->assertSame($fix, ContractSubscriber::$postPersistCalls[0][0]); + + $this->assertInstanceOf( + 'Doctrine\Tests\Models\Company\CompanyFixContract', + ContractSubscriber::$postPersistCalls[0][0] + ); + + $this->assertInstanceOf( + 'Doctrine\ORM\Event\LifecycleEventArgs', + ContractSubscriber::$postPersistCalls[0][1] + ); + } + + public function testPreUpdateListeners() + { + $fix = new CompanyFixContract(); + $fix->setFixPrice(1000); + + $this->_em->persist($fix); + $this->_em->flush(); + + ContractSubscriber::$preUpdateCalls = array(); + + $fix->setFixPrice(2000); + + $this->_em->persist($fix); + $this->_em->flush(); + + $this->assertCount(1,ContractSubscriber::$instances); + $this->assertCount(1,ContractSubscriber::$preUpdateCalls); + + $this->assertSame($fix, ContractSubscriber::$preUpdateCalls[0][0]); + + $this->assertInstanceOf( + 'Doctrine\Tests\Models\Company\CompanyFixContract', + ContractSubscriber::$preUpdateCalls[0][0] + ); + + $this->assertInstanceOf( + 'Doctrine\ORM\Event\PreUpdateEventArgs', + ContractSubscriber::$preUpdateCalls[0][1] + ); + } + + public function testPostUpdateListeners() + { + $fix = new CompanyFixContract(); + $fix->setFixPrice(1000); + + $this->_em->persist($fix); + $this->_em->flush(); + + ContractSubscriber::$postUpdateCalls = array(); + + $fix->setFixPrice(2000); + + $this->_em->persist($fix); + $this->_em->flush(); + + $this->assertCount(1,ContractSubscriber::$instances); + $this->assertCount(1,ContractSubscriber::$postUpdateCalls); + + $this->assertSame($fix, ContractSubscriber::$postUpdateCalls[0][0]); + + $this->assertInstanceOf( + 'Doctrine\Tests\Models\Company\CompanyFixContract', + ContractSubscriber::$postUpdateCalls[0][0] + ); + + $this->assertInstanceOf( + 'Doctrine\ORM\Event\LifecycleEventArgs', + ContractSubscriber::$postUpdateCalls[0][1] + ); + } + + public function testPreRemoveListeners() + { + $fix = new CompanyFixContract(); + $fix->setFixPrice(1000); + + $this->_em->persist($fix); + $this->_em->flush(); + + ContractSubscriber::$preRemoveCalls = array(); + + $this->_em->remove($fix); + $this->_em->flush(); + + $this->assertCount(1,ContractSubscriber::$instances); + $this->assertCount(1,ContractSubscriber::$preRemoveCalls); + + $this->assertSame($fix, ContractSubscriber::$preRemoveCalls[0][0]); + + $this->assertInstanceOf( + 'Doctrine\Tests\Models\Company\CompanyFixContract', + ContractSubscriber::$preRemoveCalls[0][0] + ); + + $this->assertInstanceOf( + 'Doctrine\ORM\Event\LifecycleEventArgs', + ContractSubscriber::$preRemoveCalls[0][1] + ); + } + + public function testPostRemoveListeners() + { + $fix = new CompanyFixContract(); + $fix->setFixPrice(1000); + + $this->_em->persist($fix); + $this->_em->flush(); + + ContractSubscriber::$postRemoveCalls = array(); + + $this->_em->remove($fix); + $this->_em->flush(); + + $this->assertCount(1,ContractSubscriber::$instances); + $this->assertCount(1,ContractSubscriber::$postRemoveCalls); + + $this->assertSame($fix, ContractSubscriber::$postRemoveCalls[0][0]); + + $this->assertInstanceOf( + 'Doctrine\Tests\Models\Company\CompanyFixContract', + ContractSubscriber::$postRemoveCalls[0][0] + ); + + $this->assertInstanceOf( + 'Doctrine\ORM\Event\LifecycleEventArgs', + ContractSubscriber::$postRemoveCalls[0][1] + ); + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php index a509add48..822872651 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/AbstractMappingDriverTest.php @@ -826,9 +826,9 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase $fixClass = $factory->getMetadataFor('Doctrine\Tests\Models\Company\CompanyFlexContract'); $ultraClass = $factory->getMetadataFor('Doctrine\Tests\Models\Company\CompanyFlexUltraContract'); - ContractSubscriber::$prePersistCalls = null; - ContractSubscriber::$postPersistCalls = null; - FlexUltraContractSubscriber::$prePersistCalls = null; + ContractSubscriber::$prePersistCalls = array(); + ContractSubscriber::$postPersistCalls = array(); + FlexUltraContractSubscriber::$prePersistCalls = array(); $fix = new CompanyFixContract(); $fixArg = new LifecycleEventArgs($fix, $em); @@ -862,7 +862,7 @@ abstract class AbstractMappingDriverTest extends \Doctrine\Tests\OrmTestCase $this->assertSame($ultraArg, FlexUltraContractSubscriber::$prePersistCalls[1][1]); $this->assertCount(1, ContractSubscriber::$instances); - $this->assertNull(ContractSubscriber::$postPersistCalls); + $this->assertEmpty(ContractSubscriber::$postPersistCalls); } }