From bc4e14a99f26338e83c1e643293074c064bc5bdd Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 20 May 2011 20:50:03 +0200 Subject: [PATCH 001/102] Prototype for a proxy extension that avoids loads when calling for a getter that is named after an identifier. --- lib/Doctrine/ORM/Proxy/ProxyFactory.php | 20 +++++++++++++++++++ .../ORM/Functional/LifecycleCallbackTest.php | 6 +++++- .../ORM/Functional/Ticket/DDC381Test.php | 9 +++++++-- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index 23186d7aa..5794e26f9 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -209,6 +209,11 @@ class ProxyFactory $methods .= $parameterString . ')'; $methods .= PHP_EOL . ' {' . PHP_EOL; + if ($this->isShortIdentifierGetter($method, $class)) { + $methods .= ' if ($this->__isInitialized__ === false) {' . PHP_EOL; + $methods .= ' return $this->_identifier["' . lcfirst(substr($method->getName(), 3)) . '"];' . PHP_EOL; + $methods .= ' }' . PHP_EOL; + } $methods .= ' $this->__load();' . PHP_EOL; $methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');'; $methods .= PHP_EOL . ' }' . PHP_EOL; @@ -218,6 +223,21 @@ class ProxyFactory return $methods; } + /** + * @param ReflectionMethod $method + * @param ClassMetadata $class + * @return bool + */ + private function isShortIdentifierGetter($method, $class) + { + return ( + $method->getNumberOfParameters() == 0 && + substr($method->getName(), 0, 3) == "get" && + in_array(lcfirst(substr($method->getName(), 3)), $class->identifier, true) && + (($method->getEndLine() - $method->getStartLine()) <= 4) + ); + } + /** * Generates the code for the __sleep method for a proxy class. * diff --git a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php index 1ddd7a8ff..a860ecd62 100644 --- a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php @@ -78,7 +78,7 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase $reference = $this->_em->getReference('Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity', $id); $this->assertFalse($reference->postLoadCallbackInvoked); - $reference->getId(); // trigger proxy load + $reference->getValue(); // trigger proxy load $this->assertTrue($reference->postLoadCallbackInvoked); } @@ -210,6 +210,10 @@ class LifecycleCallbackTestEntity return $this->id; } + public function getValue() { + return $this->value; + } + /** @PrePersist */ public function doStuffOnPrePersist() { $this->prePersistCallbackInvoked = true; diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC381Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC381Test.php index c25a8aa75..678135daf 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC381Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC381Test.php @@ -31,8 +31,8 @@ class DDC381Test extends \Doctrine\Tests\OrmFunctionalTestCase $entity = $this->_em->getReference('Doctrine\Tests\ORM\Functional\Ticket\DDC381Entity', $persistedId); - // explicitly load proxy - $id = $entity->getId(); + // explicitly load proxy (getId() does not trigger reload of proxy) + $id = $entity->getOtherMethod(); $data = serialize($entity); $entity = unserialize($data); @@ -55,4 +55,9 @@ class DDC381Entity { return $this->id; } + + public function getOtherMethod() + { + + } } \ No newline at end of file From d1e9bc64010c87cbfbb1746611ae2b6ec16ae4f9 Mon Sep 17 00:00:00 2001 From: kwiateusz Date: Wed, 27 Jul 2011 15:43:27 +0200 Subject: [PATCH 002/102] Now findByOne really retrieve only one entity adding limit to query. --- lib/Doctrine/ORM/EntityRepository.php | 2 +- lib/Doctrine/ORM/Persisters/BasicEntityPersister.php | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index de2689db2..2b2bee7ac 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -178,7 +178,7 @@ class EntityRepository implements ObjectRepository */ public function findOneBy(array $criteria) { - return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria); + return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($criteria, null, null, array(), 0, 1); } /** diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 19da2e200..ae16161ff 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -559,12 +559,13 @@ class BasicEntityPersister * @param $assoc The association that connects the entity to load to another entity, if any. * @param array $hints Hints for entity creation. * @param int $lockMode + * @param int $limit Limit number of results * @return object The loaded and managed entity instance or NULL if the entity can not be found. * @todo Check identity map? loadById method? Try to guess whether $criteria is the id? */ - public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0) + public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0, $limit = null) { - $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode); + $sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode, $limit); list($params, $types) = $this->expandParameters($criteria); $stmt = $this->_conn->executeQuery($sql, $params, $types); From 05fb0b913a3a02a4d21fa406c515185c22bb555c Mon Sep 17 00:00:00 2001 From: Dominik Liebler Date: Thu, 11 Aug 2011 23:03:26 +0200 Subject: [PATCH 003/102] 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 004/102] 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 005/102] 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 006/102] 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 06d56156dd6d5b388edb5b646e1feed4e2c5b76b Mon Sep 17 00:00:00 2001 From: Alain Hippolyte Date: Fri, 19 Aug 2011 06:11:58 +0300 Subject: [PATCH 007/102] Remove trailing spaces --- lib/Doctrine/ORM/Proxy/ProxyFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index 8d62ca7ab..aac74444d 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -288,7 +288,7 @@ class extends \ implements \Doctrine\ORM\Proxy\Proxy unset($this->_entityPersister, $this->_identifier); } } - + public function __sleep() From 4f22bbbc7654f6a7d58116a4eceddd58e5ecd44a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 27 Aug 2011 13:23:17 +0200 Subject: [PATCH 008/102] Add support to distribute the XSD to a given directory during build process --- build.properties.dev | 1 + build.xml | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/build.properties.dev b/build.properties.dev index 7e72d6f1e..20e91dc37 100644 --- a/build.properties.dev +++ b/build.properties.dev @@ -8,6 +8,7 @@ report.dir=reports log.archive.dir=logs project.pirum_dir= project.download_dir= +project.xsd_dir= test.phpunit_configuration_file= test.phpunit_generate_coverage=0 test.pmd_reports=0 diff --git a/build.xml b/build.xml index 7981503a5..b36457f3d 100644 --- a/build.xml +++ b/build.xml @@ -223,7 +223,12 @@ - + + + + + + @@ -235,7 +240,7 @@ - +