From 66e2a9260e1306d7ffc6bc952a0a890f30916ae4 Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 23 Oct 2011 18:39:16 +0300 Subject: [PATCH 1/4] added PreFlush lifetime event and lifecycle callback --- lib/Doctrine/ORM/Event/PreFlushEventArgs.php | 56 +++++++++++++++++++ lib/Doctrine/ORM/Events.php | 7 +++ .../ORM/Mapping/Driver/AnnotationDriver.php | 4 ++ .../Mapping/Driver/DoctrineAnnotations.php | 6 ++ lib/Doctrine/ORM/UnitOfWork.php | 35 ++++++++++++ 5 files changed, 108 insertions(+) create mode 100644 lib/Doctrine/ORM/Event/PreFlushEventArgs.php diff --git a/lib/Doctrine/ORM/Event/PreFlushEventArgs.php b/lib/Doctrine/ORM/Event/PreFlushEventArgs.php new file mode 100644 index 000000000..8ed39b6cb --- /dev/null +++ b/lib/Doctrine/ORM/Event/PreFlushEventArgs.php @@ -0,0 +1,56 @@ +. +*/ + +namespace Doctrine\ORM\Event; + +/** + * Provides event arguments for the preFlush event. + * + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.doctrine-project.com + * @since 2.0 + * @version $Revision$ + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class PreFlushEventArgs extends \Doctrine\Common\EventArgs +{ + /** + * @var EntityManager + */ + private $_em; + + //private $_entitiesToPersist = array(); + //private $_entitiesToRemove = array(); + + public function __construct($em) + { + $this->_em = $em; + } + + /** + * @return EntityManager + */ + public function getEntityManager() + { + return $this->_em; + } +} diff --git a/lib/Doctrine/ORM/Events.php b/lib/Doctrine/ORM/Events.php index e8c350aa6..8af7a9b61 100644 --- a/lib/Doctrine/ORM/Events.php +++ b/lib/Doctrine/ORM/Events.php @@ -109,6 +109,13 @@ final class Events */ const loadClassMetadata = 'loadClassMetadata'; + /** + * The preFlush event occurs when the EntityManager#flush() operation is invoked, + * but before any changes to managed entites have been calculated. This event is + * always raised right after EntityManager#flush() call. + */ + const preFlush = 'preFlush'; + /** * The onFlush event occurs when the EntityManager#flush() operation is invoked, * after any changes to managed entities have been determined but before any diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 36e3dcb01..b09cabc95 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -446,6 +446,10 @@ class AnnotationDriver implements Driver if (isset($annotations['Doctrine\ORM\Mapping\PostLoad'])) { $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::postLoad); } + + if (isset($annotations['Doctrine\ORM\Mapping\PreFlush'])) { + $metadata->addLifecycleCallback($method->getName(), \Doctrine\ORM\Events::preFlush); + } } } } diff --git a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php index e471a0d71..7f25ecbb1 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php +++ b/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php @@ -387,3 +387,9 @@ final class PostRemove implements Annotation {} * @Target("METHOD") */ final class PostLoad implements Annotation {} + +/** + * @Annotation + * @Target("METHOD") + */ +final class PreFlush implements Annotation {} diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 4366250dd..d8b640c47 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -259,6 +259,41 @@ class UnitOfWork implements PropertyChangedListener */ public function commit($entity = null) { + // Run preFlush lifecycle callback for new entities + foreach ($this->entityInsertions as $classEntity) { + $class = $this->em->getClassMetadata(get_class($classEntity)); + + // Skip class if instances are read-only + if ($class->isReadOnly) { + continue; + } + + if (isset($class->lifecycleCallbacks[Events::preFlush])) { + $class->invokeLifecycleCallbacks(Events::preFlush, $classEntity); + } + } + + // Run preFlush lifecycle callback for persisted entities + foreach ($this->identityMap as $className => $classEntities) { + $class = $this->em->getClassMetadata($className); + + // Skip class if instances are read-only + if ($class->isReadOnly) { + continue; + } + + if (isset($class->lifecycleCallbacks[Events::preFlush])) { + foreach ($classEntities as $classEntity) { + $class->invokeLifecycleCallbacks(Events::preFlush, $classEntity); + } + } + } + + // Raise preFlush + if ($this->evm->hasListeners(Events::preFlush)) { + $this->evm->dispatchEvent(Events::preFlush, new Event\PreFlushEventArgs($this->em)); + } + // Compute changes done since last commit. if ($entity === null) { $this->computeChangeSets(); From 20ed8869e43cd7f583a75339bca470212a6f199d Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 23 Oct 2011 18:39:53 +0300 Subject: [PATCH 2/4] added test for PreFlush lifetime event --- .../ORM/Functional/LifecycleCallbackTest.php | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php index 2d71541d2..8015f341f 100644 --- a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php @@ -43,6 +43,29 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals('changed from preUpdate callback!', $result[0]->value); } + public function testPreFlushCallbacksAreInvoked() + { + $entity = new LifecycleCallbackTestEntity; + $entity->value = 'hello'; + $this->_em->persist($entity); + + $this->_em->flush(); + + $this->assertTrue($entity->prePersistCallbackInvoked); + $this->assertTrue($entity->preFlushCallbackInvoked); + + $entity->preFlushCallbackInvoked = false; + $this->_em->flush(); + + $this->assertTrue($entity->preFlushCallbackInvoked); + + $entity->value = 'bye'; + $entity->preFlushCallbackInvoked = false; + $this->_em->flush(); + + $this->assertTrue($entity->preFlushCallbackInvoked); + } + public function testChangesDontGetLost() { $user = new LifecycleCallbackTestUser; @@ -190,6 +213,8 @@ class LifecycleCallbackTestEntity public $postPersistCallbackInvoked = false; public $postLoadCallbackInvoked = false; + public $preFlushCallbackInvoked = false; + /** * @Id @Column(type="integer") * @GeneratedValue(strategy="AUTO") @@ -233,6 +258,11 @@ class LifecycleCallbackTestEntity public function doStuffOnPreUpdate() { $this->value = 'changed from preUpdate callback!'; } + + /** @PreFlush */ + public function doStuffOnPreFlush() { + $this->preFlushCallbackInvoked = true; + } } /** From 91d8829c431b332d357ee8ce95e2f374d45b028e Mon Sep 17 00:00:00 2001 From: everzet Date: Sun, 23 Oct 2011 18:50:24 +0300 Subject: [PATCH 3/4] removed non-used code --- lib/Doctrine/ORM/Event/PreFlushEventArgs.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Doctrine/ORM/Event/PreFlushEventArgs.php b/lib/Doctrine/ORM/Event/PreFlushEventArgs.php index 8ed39b6cb..b86967a72 100644 --- a/lib/Doctrine/ORM/Event/PreFlushEventArgs.php +++ b/lib/Doctrine/ORM/Event/PreFlushEventArgs.php @@ -38,9 +38,6 @@ class PreFlushEventArgs extends \Doctrine\Common\EventArgs */ private $_em; - //private $_entitiesToPersist = array(); - //private $_entitiesToRemove = array(); - public function __construct($em) { $this->_em = $em; From 9c4c06c42219866b2bb3d99785b2abb2bb7f4952 Mon Sep 17 00:00:00 2001 From: everzet Date: Thu, 3 Nov 2011 16:24:47 +0200 Subject: [PATCH 4/4] optimized PreFlush (moved into computeChangeSet function) --- lib/Doctrine/ORM/UnitOfWork.php | 35 +++++---------------------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index d8b640c47..ea7138c98 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -259,36 +259,6 @@ class UnitOfWork implements PropertyChangedListener */ public function commit($entity = null) { - // Run preFlush lifecycle callback for new entities - foreach ($this->entityInsertions as $classEntity) { - $class = $this->em->getClassMetadata(get_class($classEntity)); - - // Skip class if instances are read-only - if ($class->isReadOnly) { - continue; - } - - if (isset($class->lifecycleCallbacks[Events::preFlush])) { - $class->invokeLifecycleCallbacks(Events::preFlush, $classEntity); - } - } - - // Run preFlush lifecycle callback for persisted entities - foreach ($this->identityMap as $className => $classEntities) { - $class = $this->em->getClassMetadata($className); - - // Skip class if instances are read-only - if ($class->isReadOnly) { - continue; - } - - if (isset($class->lifecycleCallbacks[Events::preFlush])) { - foreach ($classEntities as $classEntity) { - $class->invokeLifecycleCallbacks(Events::preFlush, $classEntity); - } - } - } - // Raise preFlush if ($this->evm->hasListeners(Events::preFlush)) { $this->evm->dispatchEvent(Events::preFlush, new Event\PreFlushEventArgs($this->em)); @@ -516,6 +486,11 @@ class UnitOfWork implements PropertyChangedListener return; } + // Fire PreFlush lifecycle callbacks + if (isset($class->lifecycleCallbacks[Events::preFlush])) { + $class->invokeLifecycleCallbacks(Events::preFlush, $entity); + } + $actualData = array(); foreach ($class->reflFields as $name => $refProp) { $value = $refProp->getValue($entity);