From 05fb0b913a3a02a4d21fa406c515185c22bb555c Mon Sep 17 00:00:00 2001 From: Dominik Liebler Date: Thu, 11 Aug 2011 23:03:26 +0200 Subject: [PATCH 1/5] DDC-1278 - EntityManager::clear($entity) support added new parameter $entityName for UnitOfWork::clear() removed not implemented exception in EntityManager:clear() --- lib/Doctrine/ORM/EntityManager.php | 9 ++---- lib/Doctrine/ORM/UnitOfWork.php | 51 +++++++++++++++++++----------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 0379cc435..878cbd489 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -421,16 +421,11 @@ class EntityManager implements ObjectManager * Clears the EntityManager. All entities that are currently managed * by this EntityManager become detached. * - * @param string $entityName + * @param string $entityName if given, only entities of this type will get detached */ public function clear($entityName = null) { - if ($entityName === null) { - $this->unitOfWork->clear(); - } else { - //TODO - throw new ORMException("EntityManager#clear(\$entityName) not yet implemented."); - } + $this->unitOfWork->clear($entityName); } /** diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index d62eb62c7..1b29d65f3 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1790,28 +1790,41 @@ class UnitOfWork implements PropertyChangedListener /** * Clears the UnitOfWork. + * + * @param strin $entityName if given, only entities of this type will get detached */ - public function clear() + public function clear($entityName = null) { - $this->identityMap = - $this->entityIdentifiers = - $this->originalEntityData = - $this->entityChangeSets = - $this->entityStates = - $this->scheduledForDirtyCheck = - $this->entityInsertions = - $this->entityUpdates = - $this->entityDeletions = - $this->collectionDeletions = - $this->collectionUpdates = - $this->extraUpdates = - $this->orphanRemovals = array(); - if ($this->commitOrderCalculator !== null) { - $this->commitOrderCalculator->clear(); - } + if ($entityName === null) { + $this->identityMap = + $this->entityIdentifiers = + $this->originalEntityData = + $this->entityChangeSets = + $this->entityStates = + $this->scheduledForDirtyCheck = + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->collectionDeletions = + $this->collectionUpdates = + $this->extraUpdates = + $this->orphanRemovals = array(); + if ($this->commitOrderCalculator !== null) { + $this->commitOrderCalculator->clear(); + } - if ($this->evm->hasListeners(Events::onClear)) { - $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em)); + if ($this->evm->hasListeners(Events::onClear)) { + $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em)); + } + } else { + $visited = array(); + foreach ($this->identityMap as $className => $entities) { + if ($className === $entityName) { + foreach ($entities as $entity) { + $this->doDetach($entity, $visited); + } + } + } } } From 745535d269c81635ad9ef1c922b7371b44175541 Mon Sep 17 00:00:00 2001 From: Dominik Liebler Date: Fri, 12 Aug 2011 20:15:32 +0200 Subject: [PATCH 2/5] fixed typo --- lib/Doctrine/ORM/UnitOfWork.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 1b29d65f3..841e515b6 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1791,7 +1791,7 @@ class UnitOfWork implements PropertyChangedListener /** * Clears the UnitOfWork. * - * @param strin $entityName if given, only entities of this type will get detached + * @param string $entityName if given, only entities of this type will get detached */ public function clear($entityName = null) { From 25f5ff0ca1e0da0753dbcc883a67477027f20b51 Mon Sep 17 00:00:00 2001 From: Dominik Liebler Date: Sat, 13 Aug 2011 20:22:23 +0200 Subject: [PATCH 3/5] DDC-1278 - EntityManager::clear($entity) support cascade detach operation only on entity name entities --- lib/Doctrine/ORM/UnitOfWork.php | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 841e515b6..64bca53cf 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1515,8 +1515,9 @@ class UnitOfWork implements PropertyChangedListener * * @param object $entity * @param array $visited + * @param string $entityName detach only entities of this type when given */ - private function doDetach($entity, array &$visited) + private function doDetach($entity, array &$visited, $entityName = null) { $oid = spl_object_hash($entity); if (isset($visited[$oid])) { @@ -1539,7 +1540,7 @@ class UnitOfWork implements PropertyChangedListener return; } - $this->cascadeDetach($entity, $visited); + $this->cascadeDetach($entity, $visited, $entityName); } /** @@ -1617,8 +1618,9 @@ class UnitOfWork implements PropertyChangedListener * * @param object $entity * @param array $visited + * @param string $entityName detach only entities of this type when given */ - private function cascadeDetach($entity, array &$visited) + private function cascadeDetach($entity, array &$visited, $entityName = null) { $class = $this->em->getClassMetadata(get_class($entity)); foreach ($class->associationMappings as $assoc) { @@ -1632,10 +1634,14 @@ class UnitOfWork implements PropertyChangedListener $relatedEntities = $relatedEntities->unwrap(); } foreach ($relatedEntities as $relatedEntity) { - $this->doDetach($relatedEntity, $visited); + if ($entityName === null || get_class($relatedEntity) == $entityName) { + $this->doDetach($relatedEntity, $visited, $entityName); + } } } else if ($relatedEntities !== null) { - $this->doDetach($relatedEntities, $visited); + if ($entityName === null || get_class($relatedEntities) == $entityName) { + $this->doDetach($relatedEntities, $visited, $entityName); + } } } } @@ -1812,20 +1818,20 @@ class UnitOfWork implements PropertyChangedListener if ($this->commitOrderCalculator !== null) { $this->commitOrderCalculator->clear(); } - - if ($this->evm->hasListeners(Events::onClear)) { - $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em)); - } } else { $visited = array(); foreach ($this->identityMap as $className => $entities) { if ($className === $entityName) { foreach ($entities as $entity) { - $this->doDetach($entity, $visited); + $this->doDetach($entity, $visited, $entityName); } } } } + + if ($this->evm->hasListeners(Events::onClear)) { + $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em)); + } } /** From 6e47d7b16d91c2859bf23bdf98c2c6c5854159a6 Mon Sep 17 00:00:00 2001 From: Dominik Liebler Date: Sun, 14 Aug 2011 16:12:12 +0200 Subject: [PATCH 4/5] DDC-1278 - EntityManager::clear($entity) support added test case and modified test data CmsUser to cascade detach address and articles (testing collections and single entites) --- lib/Doctrine/ORM/UnitOfWork.php | 23 ++++----- tests/Doctrine/Tests/Models/CMS/CmsUser.php | 4 +- .../ORM/Functional/BasicFunctionalTest.php | 50 +++++++++++++++++++ 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 64bca53cf..1e4310efc 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1515,9 +1515,9 @@ class UnitOfWork implements PropertyChangedListener * * @param object $entity * @param array $visited - * @param string $entityName detach only entities of this type when given + * @param boolean $noCascade if true, don't cascade detach operation */ - private function doDetach($entity, array &$visited, $entityName = null) + private function doDetach($entity, array &$visited, $noCascade = false) { $oid = spl_object_hash($entity); if (isset($visited[$oid])) { @@ -1539,8 +1539,10 @@ class UnitOfWork implements PropertyChangedListener case self::STATE_DETACHED: return; } - - $this->cascadeDetach($entity, $visited, $entityName); + + if (!$noCascade) { + $this->cascadeDetach($entity, $visited); + } } /** @@ -1618,9 +1620,8 @@ class UnitOfWork implements PropertyChangedListener * * @param object $entity * @param array $visited - * @param string $entityName detach only entities of this type when given */ - private function cascadeDetach($entity, array &$visited, $entityName = null) + private function cascadeDetach($entity, array &$visited) { $class = $this->em->getClassMetadata(get_class($entity)); foreach ($class->associationMappings as $assoc) { @@ -1634,14 +1635,10 @@ class UnitOfWork implements PropertyChangedListener $relatedEntities = $relatedEntities->unwrap(); } foreach ($relatedEntities as $relatedEntity) { - if ($entityName === null || get_class($relatedEntity) == $entityName) { - $this->doDetach($relatedEntity, $visited, $entityName); - } + $this->doDetach($relatedEntity, $visited); } } else if ($relatedEntities !== null) { - if ($entityName === null || get_class($relatedEntities) == $entityName) { - $this->doDetach($relatedEntities, $visited, $entityName); - } + $this->doDetach($relatedEntities, $visited); } } } @@ -1823,7 +1820,7 @@ class UnitOfWork implements PropertyChangedListener foreach ($this->identityMap as $className => $entities) { if ($className === $entityName) { foreach ($entities as $entity) { - $this->doDetach($entity, $visited, $entityName); + $this->doDetach($entity, $visited, true); } } } diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUser.php b/tests/Doctrine/Tests/Models/CMS/CmsUser.php index d9ac982ff..6b298c477 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUser.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUser.php @@ -35,7 +35,7 @@ class CmsUser */ public $phonenumbers; /** - * @OneToMany(targetEntity="CmsArticle", mappedBy="user") + * @OneToMany(targetEntity="CmsArticle", mappedBy="user", cascade={"detach"}) */ public $articles; /** @@ -43,7 +43,7 @@ class CmsUser */ public $address; /** - * @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge"}) + * @ManyToMany(targetEntity="CmsGroup", inversedBy="users", cascade={"persist", "merge", "detach"}) * @JoinTable(name="cms_users_groups", * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, * inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")} diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index d94aa253d..e189e8408 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -981,4 +981,54 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertTrue($article->user->__isInitialized__, "...but its initialized!"); $this->assertEquals($qc+2, $this->getCurrentQueryCount()); } + + /** + * @group DDC-1278 + */ + public function testClearWithEntityName() + { + $user = new CmsUser; + $user->name = 'Dominik'; + $user->username = 'domnikl'; + $user->status = 'developer'; + + $address = new CmsAddress(); + $address->city = "Springfield"; + $address->zip = "12354"; + $address->country = "Germany"; + $address->street = "Foo Street"; + $address->user = $user; + $user->address = $address; + + $article1 = new CmsArticle(); + $article1->topic = 'Foo'; + $article1->text = 'Foo Text'; + + $article2 = new CmsArticle(); + $article2->topic = 'Bar'; + $article2->text = 'Bar Text'; + + $user->addArticle($article1); + $user->addArticle($article2); + + $this->_em->persist($article1); + $this->_em->persist($article2); + $this->_em->persist($address); + $this->_em->persist($user); + $this->_em->flush(); + + $unitOfWork = $this->_em->getUnitOfWork(); + + $this->_em->clear('Doctrine\Tests\Models\CMS\CmsUser'); + + $this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($user)); + + $this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $unitOfWork->getEntityState($address)); + $this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $unitOfWork->getEntityState($article1)); + $this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $unitOfWork->getEntityState($article2)); + + $this->_em->clear(); + + $this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_DETACHED, $unitOfWork->getEntityState($address)); + } } From 939fbf9c24f45ea01491bf1dd72921994c7f1656 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 16 Oct 2011 22:45:06 +0200 Subject: [PATCH 5/5] DDC-1278 - Clean up event handling of new clear functionality. --- lib/Doctrine/ORM/Event/OnClearEventArgs.php | 28 ++++++++++++++++++++- lib/Doctrine/ORM/UnitOfWork.php | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Event/OnClearEventArgs.php b/lib/Doctrine/ORM/Event/OnClearEventArgs.php index ad89fbc90..60ce4b3eb 100644 --- a/lib/Doctrine/ORM/Event/OnClearEventArgs.php +++ b/lib/Doctrine/ORM/Event/OnClearEventArgs.php @@ -36,12 +36,18 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs */ private $em; + /** + * @var string + */ + private $entityClass; + /** * @param \Doctrine\ORM\EntityManager $em */ - public function __construct($em) + public function __construct($em, $entityClass = null) { $this->em = $em; + $this->entityClass = $entityClass; } /** @@ -51,4 +57,24 @@ class OnClearEventArgs extends \Doctrine\Common\EventArgs { return $this->em; } + + /** + * Name of the entity class that is cleared, or empty if all are cleared. + * + * @return string + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Check if event clears all entities. + * + * @return bool + */ + public function clearsAllEntities() + { + return $this->entityClass === null; + } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 1b291d717..8f5977e5d 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1879,7 +1879,7 @@ class UnitOfWork implements PropertyChangedListener } if ($this->evm->hasListeners(Events::onClear)) { - $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em)); + $this->evm->dispatchEvent(Events::onClear, new Event\OnClearEventArgs($this->em, $entityName)); } }