From bc4e14a99f26338e83c1e643293074c064bc5bdd Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 20 May 2011 20:50:03 +0200 Subject: [PATCH 01/42] 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 02/42] 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 03/42] 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 04/42] 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 05/42] 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 06/42] 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 07/42] 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 d3143860609b37aa628e6434af73f812cbeec188 Mon Sep 17 00:00:00 2001 From: Nadav Date: Fri, 26 Aug 2011 07:42:16 +0300 Subject: [PATCH 08/42] we can (now) transform it into IS NULL --- lib/Doctrine/ORM/EntityRepository.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index de2689db2..1aa2faef7 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -204,11 +204,6 @@ class EntityRepository implements ObjectRepository ); } - if ( !isset($arguments[0])) { - // we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL. - throw ORMException::findByRequiresParameter($method.$by); - } - $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) { From 5fc6277d3f2b469eb2e4260f7cb6aa0665287009 Mon Sep 17 00:00:00 2001 From: Nadav Date: Fri, 26 Aug 2011 07:51:29 +0300 Subject: [PATCH 09/42] Oops, shouldn't have removed the condition completely... checking a parameter is provided --- lib/Doctrine/ORM/EntityRepository.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index 1aa2faef7..3e1b83df6 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -203,6 +203,11 @@ class EntityRepository implements ObjectRepository "either findBy or findOneBy!" ); } + + + if (count($arguments) === 0) { + throw ORMException::findByRequiresParameter($method.$by); + } $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); From 2e389e00d40dd68462ca170ac6ee3942297b412c Mon Sep 17 00:00:00 2001 From: Nadav Date: Fri, 26 Aug 2011 08:15:28 +0300 Subject: [PATCH 10/42] Removed blank line, used empty() instead of the count() check --- lib/Doctrine/ORM/EntityRepository.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index 3e1b83df6..28ba1f206 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -203,9 +203,8 @@ class EntityRepository implements ObjectRepository "either findBy or findOneBy!" ); } - - if (count($arguments) === 0) { + if (empty($arguments)) { throw ORMException::findByRequiresParameter($method.$by); } From 90725fa5297a5581b3f045bc6fc480f1f20aa9c8 Mon Sep 17 00:00:00 2001 From: Alan Bem Date: Tue, 20 Sep 2011 11:10:59 +0200 Subject: [PATCH 11/42] fixed wrong on-delete XML Schema mapping --- doctrine-mapping.xsd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index 627ddf460..e8e21f193 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -150,7 +150,7 @@ - + From 2b334977f536e0d4f473ed1faa748d8c37de97db Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 20 Sep 2011 14:59:32 +0200 Subject: [PATCH 12/42] Add "return $this" to generated methods to get a fluent Enttity class --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index e53505f98..bfe562fb2 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -115,10 +115,12 @@ public function () * * * @param $ + * @return */ public function ($) { $this-> = $; +return $this; }'; private static $_addMethodTemplate = @@ -734,7 +736,8 @@ public function () '' => $variableType, '' => Inflector::camelize($fieldName), '' => $methodName, - '' => $fieldName + '' => $fieldName, + '' => $this->_getClassName($metadata) ); $method = str_replace( From 944f802d79baaf5eb16aaa86f8b7698e3fa9af17 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 20 Sep 2011 15:35:16 +0200 Subject: [PATCH 13/42] Correct indentation --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index bfe562fb2..96c3b0136 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -737,7 +737,7 @@ public function () '' => Inflector::camelize($fieldName), '' => $methodName, '' => $fieldName, - '' => $this->_getClassName($metadata) + '' => $this->_getClassName($metadata) ); $method = str_replace( From 01d900d5d7b4d60616d6c2565ab46eaffb93046b Mon Sep 17 00:00:00 2001 From: Benjamin Date: Tue, 20 Sep 2011 15:50:32 +0200 Subject: [PATCH 14/42] tab <-> spaces --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 96c3b0136..eb00cc8fc 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -737,7 +737,7 @@ public function () '' => Inflector::camelize($fieldName), '' => $methodName, '' => $fieldName, - '' => $this->_getClassName($metadata) + '' => $this->_getClassName($metadata) ); $method = str_replace( From b28af2e5278013eaf508a69127676e2690337e3d Mon Sep 17 00:00:00 2001 From: docteurklein Date: Tue, 27 Sep 2011 10:36:32 +0200 Subject: [PATCH 15/42] added fluent pattern to Query\Expr\Base::add* methods --- lib/Doctrine/ORM/Query/Expr/Base.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Expr/Base.php b/lib/Doctrine/ORM/Query/Expr/Base.php index abe7e54be..abc2c210e 100644 --- a/lib/Doctrine/ORM/Query/Expr/Base.php +++ b/lib/Doctrine/ORM/Query/Expr/Base.php @@ -45,12 +45,14 @@ abstract class Base { $this->addMultiple($args); } - + public function addMultiple($args = array()) { foreach ((array) $args as $arg) { $this->add($arg); } + + return $this; } public function add($arg) @@ -67,6 +69,8 @@ abstract class Base $this->_parts[] = $arg; } + + return $this; } public function count() @@ -79,7 +83,7 @@ abstract class Base if ($this->count() == 1) { return (string) $this->_parts[0]; } - + return $this->_preSeparator . implode($this->_separator, $this->_parts) . $this->_postSeparator; } -} \ No newline at end of file +} From d24f2881496fcf0faa6c7a1a22f07e3fc001d935 Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Thu, 29 Sep 2011 09:31:06 +0200 Subject: [PATCH 16/42] Better error handling on missing assigned id --- lib/Doctrine/ORM/Id/AssignedGenerator.php | 8 ++++---- lib/Doctrine/ORM/ORMException.php | 17 ++++++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/Doctrine/ORM/Id/AssignedGenerator.php b/lib/Doctrine/ORM/Id/AssignedGenerator.php index 05c3790af..0143a157f 100644 --- a/lib/Doctrine/ORM/Id/AssignedGenerator.php +++ b/lib/Doctrine/ORM/Id/AssignedGenerator.php @@ -53,14 +53,14 @@ class AssignedGenerator extends AbstractIdGenerator if (!$em->getUnitOfWork()->isInIdentityMap($value)) { throw ORMException::entityMissingForeignAssignedId($entity, $value); } - + // NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced. $identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value)); } else { $identifier[$idField] = $value; } } else { - throw ORMException::entityMissingAssignedId($entity); + throw ORMException::entityMissingAssignedIdForField($entity, $idField); } } } else { @@ -71,7 +71,7 @@ class AssignedGenerator extends AbstractIdGenerator if (!$em->getUnitOfWork()->isInIdentityMap($value)) { throw ORMException::entityMissingForeignAssignedId($entity, $value); } - + // NOTE: Single Columns as associated identifiers only allowed - this constraint it is enforced. $identifier[$idField] = current($em->getUnitOfWork()->getEntityIdentifier($value)); } else { @@ -81,7 +81,7 @@ class AssignedGenerator extends AbstractIdGenerator throw ORMException::entityMissingAssignedId($entity); } } - + return $identifier; } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/ORMException.php b/lib/Doctrine/ORM/ORMException.php index aa9657552..b28c8d32d 100644 --- a/lib/Doctrine/ORM/ORMException.php +++ b/lib/Doctrine/ORM/ORMException.php @@ -34,7 +34,7 @@ class ORMException extends Exception return new self("It's a requirement to specify a Metadata Driver and pass it ". "to Doctrine\ORM\Configuration::setMetadataDriverImpl()."); } - + public static function entityMissingForeignAssignedId($entity, $relatedEntity) { return new self( @@ -50,11 +50,18 @@ class ORMException extends Exception { return new self("Entity of type " . get_class($entity) . " is missing an assigned ID. " . "The identifier generation strategy for this entity requires the ID field to be populated before ". - "EntityManager#persist() is called. If you want automatically generated identifiers instead " . + "EntityManager#persist() is called. If you want automatically generated identifiers instead " . + "you need to adjust the metadata mapping accordingly." + ); + } + public static function entityMissingAssignedIdForField($entity, $field) + { + return new self("Entity of type " . get_class($entity) . " is missing an assigned ID for field '" . $field . "'. " . + "The identifier generation strategy for this entity requires the ID field to be populated before ". + "EntityManager#persist() is called. If you want automatically generated identifiers instead " . "you need to adjust the metadata mapping accordingly." ); } - public static function unrecognizedField($field) { return new self("Unrecognized field: $field"); @@ -130,8 +137,8 @@ class ORMException extends Exception "Unknown Entity namespace alias '$entityNamespaceAlias'." ); } - - public static function invalidEntityRepository($className) + + public static function invalidEntityRepository($className) { return new self("Invalid repository class '".$className."'. ". "it must be a Doctrine\ORM\EntityRepository."); From 689aaef4dc79fa8207ad2550711db41315a0baab Mon Sep 17 00:00:00 2001 From: Christian Raue Date: Mon, 10 Oct 2011 09:44:17 +0300 Subject: [PATCH 17/42] added missing type hint --- 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 ea516f3d0..85a5a3f2f 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -635,7 +635,7 @@ class UnitOfWork implements PropertyChangedListener * @param object $entity The entity for which to (re)calculate the change set. * @throws InvalidArgumentException If the passed entity is not MANAGED. */ - public function recomputeSingleEntityChangeSet($class, $entity) + public function recomputeSingleEntityChangeSet(ClassMetadata $class, $entity) { $oid = spl_object_hash($entity); From 97a6caf059243643835e4ccd4df311ebbdea67a3 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 11 Oct 2011 13:22:26 +0200 Subject: [PATCH 18/42] Update Common to latest to have the current Persistence Interface --- lib/vendor/doctrine-common | 2 +- tests/Doctrine/Tests/ORM/Functional/QueryCacheTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/vendor/doctrine-common b/lib/vendor/doctrine-common index ef431a148..b2fd909b4 160000 --- a/lib/vendor/doctrine-common +++ b/lib/vendor/doctrine-common @@ -1 +1 @@ -Subproject commit ef431a14852d7e8f2d0ea789487509ab266e5ce2 +Subproject commit b2fd909b4b5476df01744c9d34c7a23723a687b6 diff --git a/tests/Doctrine/Tests/ORM/Functional/QueryCacheTest.php b/tests/Doctrine/Tests/ORM/Functional/QueryCacheTest.php index eb7d662d6..13a774b76 100644 --- a/tests/Doctrine/Tests/ORM/Functional/QueryCacheTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/QueryCacheTest.php @@ -104,7 +104,7 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux'); - $cache = $this->getMock('Doctrine\Common\Cache\ArrayCache', array('doFetch', 'doSave')); + $cache = $this->getMock('Doctrine\Common\Cache\ArrayCache', array('doFetch', 'doSave', 'doGetStats')); $cache->expects($this->at(0)) ->method('doFetch') ->with($this->isType('string')) @@ -135,7 +135,7 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase ->will($this->returnValue($sqlExecMock)); $cache = $this->getMock('Doctrine\Common\Cache\CacheProvider', - array('doFetch', 'doContains', 'doSave', 'doDelete', 'doFlush')); + array('doFetch', 'doContains', 'doSave', 'doDelete', 'doFlush', 'doGetStats')); $cache->expects($this->once()) ->method('doFetch') ->with($this->isType('string')) From 8d1b852aa22569fc62c0b9a3ea9b13713b08de84 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 15 Oct 2011 17:31:09 +0200 Subject: [PATCH 19/42] Added tests for not loading the entity + fixed a test --- .../Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php | 5 ++++- tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1238Test.php | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php index a860ecd62..35eb5443c 100644 --- a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php @@ -78,6 +78,9 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase $reference = $this->_em->getReference('Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity', $id); $this->assertFalse($reference->postLoadCallbackInvoked); + $reference->getId(); // doesn't trigger proxy load + $this->assertFalse($reference->postLoadCallbackInvoked); + $reference->getValue(); // trigger proxy load $this->assertTrue($reference->postLoadCallbackInvoked); } @@ -278,4 +281,4 @@ class LifecycleListenerPreUpdate { $eventArgs->setNewValue('name', 'Bob'); } -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1238Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1238Test.php index 93a89818e..467577a43 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1238Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1238Test.php @@ -52,6 +52,8 @@ class DDC1238Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->flush(); $this->_em->clear(); + // force proxy load, getId() doesn't work anymore + $user->getName(); $userId = $user->getId(); $this->_em->clear(); @@ -60,6 +62,8 @@ class DDC1238Test extends \Doctrine\Tests\OrmFunctionalTestCase $user2 = $this->_em->getReference(__NAMESPACE__ . '\\DDC1238User', $userId); + // force proxy load, getId() doesn't work anymore + $user->getName(); $this->assertNull($user->getId(), "Now this is null, we already have a user instance of that type"); } } From cb21f3c5ffa731e85293fa2086c72dc61f1e4c5d Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 15 Oct 2011 17:47:09 +0200 Subject: [PATCH 20/42] DDC-1414 - Missing push to $newNodes --- lib/Doctrine/ORM/UnitOfWork.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index f53ec67a7..d3cad211c 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -871,6 +871,7 @@ class UnitOfWork implements PropertyChangedListener $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); if ( ! $calc->hasClass($targetClass->name)) { $calc->addClass($targetClass); + $newNodes[] = $targetClass; } $calc->addDependency($targetClass, $class); // If the target class has mapped subclasses, From 52cea015637b0969a1beca96c126cddc0a404f71 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 15 Oct 2011 17:53:04 +0200 Subject: [PATCH 21/42] DDC-1411 - Fixed onDelete handling in EntityGenerator --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 599e35b3a..db353c3c1 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -791,7 +791,7 @@ public function () } if (isset($joinColumn['onDelete'])) { - $joinColumnAnnot[] = 'onDelete=' . ($joinColumn['onDelete'] ? 'true' : 'false'); + $joinColumnAnnot[] = 'onDelete="' . ($joinColumn['onDelete'] . '"'); } if (isset($joinColumn['columnDefinition'])) { From d46352da0106278ce3b271bca907a381254c42d8 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 15 Oct 2011 17:58:00 +0200 Subject: [PATCH 22/42] Fixed tests + added dedicated tests for proxy loading and identifiers --- .../ORM/Functional/LifecycleCallbackTest.php | 3 --- .../ORM/Functional/ReferenceProxyTest.php | 24 +++++++++++++++++++ .../ORM/Proxy/ProxyClassGeneratorTest.php | 5 ++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php index 35eb5443c..2d71541d2 100644 --- a/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/LifecycleCallbackTest.php @@ -78,9 +78,6 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase $reference = $this->_em->getReference('Doctrine\Tests\ORM\Functional\LifecycleCallbackTestEntity', $id); $this->assertFalse($reference->postLoadCallbackInvoked); - $reference->getId(); // doesn't trigger proxy load - $this->assertFalse($reference->postLoadCallbackInvoked); - $reference->getValue(); // trigger proxy load $this->assertTrue($reference->postLoadCallbackInvoked); } diff --git a/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php b/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php index 8ecb389af..b89f3d04e 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php @@ -147,4 +147,28 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertTrue($entity->wakeUp, "Loading the proxy should call __wakeup()."); } + + public function testDoNotInitializeProxyOnGettingTheIdentifier() + { + $id = $this->createProduct(); + + /* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */ + $entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id); + + $this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy."); + $this->assertEquals($id, $entity->getId()); + $this->assertFalse($entity->__isInitialized__, "Getting the identifier doesn't initialize the proxy."); + } + + public function testInitializeProxyOnGettingSomethingOtherThanTheIdentifier() + { + $id = $this->createProduct(); + + /* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */ + $entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id); + + $this->assertFalse($entity->__isInitialized__, "Pre-Condition: Object is unitialized proxy."); + $this->assertEquals('Doctrine Cookbook', $entity->getName()); + $this->assertTrue($entity->__isInitialized__, "Getting something other than the identifier initializes the proxy."); + } } diff --git a/tests/Doctrine/Tests/ORM/Proxy/ProxyClassGeneratorTest.php b/tests/Doctrine/Tests/ORM/Proxy/ProxyClassGeneratorTest.php index 6257dbe70..39bdcfe5a 100644 --- a/tests/Doctrine/Tests/ORM/Proxy/ProxyClassGeneratorTest.php +++ b/tests/Doctrine/Tests/ORM/Proxy/ProxyClassGeneratorTest.php @@ -73,12 +73,13 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase $persister = $this->_getMockPersister(); $this->_uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister); $proxy = $this->_proxyFactory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $identifier); + $persister->expects($this->atLeastOnce()) ->method('load') ->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass)) ->will($this->returnValue(new \stdClass())); // fake return of entity instance - $proxy->getId(); $proxy->getDescription(); + $proxy->getProduct(); } public function testReferenceProxyRespectsMethodsParametersTypeHinting() @@ -179,4 +180,4 @@ class SleepClass { return array('id'); } -} \ No newline at end of file +} From 6f3667201c981be7e4341435ddde5850edd3116a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 15 Oct 2011 18:11:14 +0200 Subject: [PATCH 23/42] Add @ignore and @internal to UnitOfWork#computeChangeSet --- lib/Doctrine/ORM/UnitOfWork.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index d3cad211c..c5c690e1c 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -398,6 +398,8 @@ class UnitOfWork implements PropertyChangedListener * If a PersistentCollection has been de-referenced in a fully MANAGED entity, * then this collection is marked for deletion. * + * @ignore + * @internal Don't call from the outside. * @param ClassMetadata $class The class descriptor of the entity. * @param object $entity The entity for which to compute the changes. */ From 08716d9f72996bab8c71a8cc8bda72d3925e8fb1 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 15 Oct 2011 19:14:30 +0200 Subject: [PATCH 24/42] DDC-1383 - Proxy Generation in merge was flawed with inheritance --- lib/Doctrine/ORM/UnitOfWork.php | 14 ++- .../ORM/Functional/Ticket/DDC1383Test.php | 99 +++++++++++++++++++ 2 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1383Test.php diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index c5c690e1c..2f064e975 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1473,11 +1473,17 @@ class UnitOfWork implements PropertyChangedListener if ($this->getEntityState($other, self::STATE_DETACHED) == self::STATE_MANAGED) { $prop->setValue($managedCopy, $other); } else { + $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']); - $id = $targetClass->getIdentifierValues($other); - $proxy = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $id); - $prop->setValue($managedCopy, $proxy); - $this->registerManaged($proxy, $id, array()); + $relatedId = $targetClass->getIdentifierValues($other); + + if ($targetClass->subClasses) { + $entity = $this->em->find($targetClass->name, $relatedId); + } else { + $proxy = $this->em->getProxyFactory()->getProxy($assoc2['targetEntity'], $relatedId); + $prop->setValue($managedCopy, $proxy); + $this->registerManaged($proxy, $relatedId, array()); + } } } } else { diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1383Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1383Test.php new file mode 100644 index 000000000..6a8cf483a --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1383Test.php @@ -0,0 +1,99 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1383AbstractEntity'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC1383Entity'), + )); + } catch(\Exception $ignored) {} + } + + public function testFailingCase() + { + $parent = new DDC1383Entity(); + $child = new DDC1383Entity(); + + $child->setReference($parent); + + $this->_em->persist($parent); + $this->_em->persist($child); + + $id = $child->getId(); + + $this->_em->flush(); + $this->_em->clear(); + + // Try merging the parent entity + $child = $this->_em->merge($child); + $parent = $child->getReference(); + + // Parent is not instance of the abstract class + self::assertTrue($parent instanceof DDC1383AbstractEntity, + "Entity class is " . get_class($parent) . ', "DDC1383AbstractEntity" was expected'); + + // Parent is NOT instance of entity + self::assertTrue($parent instanceof DDC1383Entity, + "Entity class is " . get_class($parent) . ', "DDC1383Entity" was expected'); + } +} + +/** + * @Entity + * @InheritanceType("JOINED") + * @DiscriminatorColumn(name="discr", type="integer") + * @DiscriminatorMap({1 = "DDC1383Entity"}) + */ +abstract class DDC1383AbstractEntity +{ + /** + * @Id + * @Column(type="integer") + * @GeneratedValue + */ + protected $id; + + public function getId() + { + return $this->id; + } + + public function setId($id) + { + $this->id = $id; + } +} + +/** + * @Entity + */ +class DDC1383Entity extends DDC1383AbstractEntity +{ + /** + * @ManyToOne(targetEntity="DDC1383AbstractEntity") + */ + protected $reference; + + public function getReference() + { + return $this->reference; + } + + public function setReference(DDC1383AbstractEntity $reference) + { + $this->reference = $reference; + } +} \ No newline at end of file From fdb9fb1c2b5d35974188eb9344d87e0a41c75ca8 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 15 Oct 2011 19:23:36 +0200 Subject: [PATCH 25/42] AssignedGenerator can always tell what field is missing an id --- lib/Doctrine/ORM/Id/AssignedGenerator.php | 4 ++-- lib/Doctrine/ORM/ORMException.php | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/Doctrine/ORM/Id/AssignedGenerator.php b/lib/Doctrine/ORM/Id/AssignedGenerator.php index 0143a157f..90a35fa12 100644 --- a/lib/Doctrine/ORM/Id/AssignedGenerator.php +++ b/lib/Doctrine/ORM/Id/AssignedGenerator.php @@ -78,10 +78,10 @@ class AssignedGenerator extends AbstractIdGenerator $identifier[$idField] = $value; } } else { - throw ORMException::entityMissingAssignedId($entity); + throw ORMException::entityMissingAssignedIdForField($entity, $idField); } } return $identifier; } -} \ No newline at end of file +} diff --git a/lib/Doctrine/ORM/ORMException.php b/lib/Doctrine/ORM/ORMException.php index b28c8d32d..c156893c5 100644 --- a/lib/Doctrine/ORM/ORMException.php +++ b/lib/Doctrine/ORM/ORMException.php @@ -46,14 +46,6 @@ class ORMException extends Exception ); } - public static function entityMissingAssignedId($entity) - { - return new self("Entity of type " . get_class($entity) . " is missing an assigned ID. " . - "The identifier generation strategy for this entity requires the ID field to be populated before ". - "EntityManager#persist() is called. If you want automatically generated identifiers instead " . - "you need to adjust the metadata mapping accordingly." - ); - } public static function entityMissingAssignedIdForField($entity, $field) { return new self("Entity of type " . get_class($entity) . " is missing an assigned ID for field '" . $field . "'. " . From cab154b8737ed843e3c425501dd111c5185e8bcf Mon Sep 17 00:00:00 2001 From: lenar Date: Wed, 31 Aug 2011 10:28:02 +0300 Subject: [PATCH 26/42] identifier referencing foreign entity can be defined in parent class too --- lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 4b43e3070..248e4a2f9 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -308,6 +308,10 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface if ($parent && $parent->isInheritanceTypeSingleTable()) { $class->setPrimaryTable($parent->table); } + + if ($parent && $parent->containsForeignIdentifier) { + $class->containsForeignIdentifier = true; + } $class->setParentClasses($visited); From 3dc30dee1159bbd9598da6c888bbcee6aaa2f132 Mon Sep 17 00:00:00 2001 From: lenar Date: Thu, 1 Sep 2011 11:29:12 +0300 Subject: [PATCH 27/42] use the correct targetEntity --- lib/Doctrine/ORM/Persisters/BasicEntityPersister.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index a0382a04d..db9f731d2 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -823,7 +823,7 @@ class BasicEntityPersister if (isset($sourceClass->associationMappings[$field])) { $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); - $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; + $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; } $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; @@ -847,7 +847,7 @@ class BasicEntityPersister if (isset($sourceClass->associationMappings[$field])) { $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); - $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; + $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; } $criteria[$quotedJoinTable . "." . $relationKeyColumn] = $value; @@ -1355,7 +1355,7 @@ class BasicEntityPersister if (isset($sourceClass->associationMappings[$field])) { $value = $this->_em->getUnitOfWork()->getEntityIdentifier($value); - $value = $value[$this->_em->getClassMetadata($assoc['targetEntity'])->identifier[0]]; + $value = $value[$this->_em->getClassMetadata($sourceClass->associationMappings[$field]['targetEntity'])->identifier[0]]; } $criteria[$tableAlias . "." . $targetKeyColumn] = $value; From a82bffbfc99101319883423c4bdfbf88fd7cb92a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 15 Oct 2011 20:31:56 +0200 Subject: [PATCH 28/42] Make SchemaValidator catch errors such as very invalid schema using only part of the primary key for join columns --- lib/Doctrine/ORM/Tools/SchemaValidator.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/SchemaValidator.php b/lib/Doctrine/ORM/Tools/SchemaValidator.php index 8acaa0b54..a7f8e3a1c 100644 --- a/lib/Doctrine/ORM/Tools/SchemaValidator.php +++ b/lib/Doctrine/ORM/Tools/SchemaValidator.php @@ -152,6 +152,17 @@ class SchemaValidator "has to be a primary key column."; } } + + if (count($targetClass->identifier) != count($assoc['joinTable']['inverseJoinColumns'])) { + $ce[] = "The inverse join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " . + "have to match to ALL identifier columns of the target entity '". $targetClass->name . "'"; + } + + if (count($class->identifier) != count($assoc['joinTable']['joinColumns'])) { + $ce[] = "The join columns of the many-to-many table '" . $assoc['joinTable']['name'] . "' " . + "have to match to ALL identifier columns of the source entity '". $class->name . "'"; + } + } else if ($assoc['type'] & ClassMetadataInfo::TO_ONE) { foreach ($assoc['joinColumns'] AS $joinColumn) { $targetClass = $cmf->getMetadataFor($assoc['targetEntity']); @@ -167,6 +178,11 @@ class SchemaValidator "has to be a primary key column."; } } + + if (count($class->identifier) != count($assoc['joinColumns'])) { + $ce[] = "The join columns of the association '" . $assoc['fieldName'] . "' " . + "have to match to ALL identifier columns of the source entity '". $class->name . "'"; + } } } From 7b71b3284ded7e2b35fc5e8084b20f9f2f424542 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 15 Oct 2011 20:41:07 +0200 Subject: [PATCH 29/42] Fix failing test due to EntityGenerator assuming beginning with 2.2 the AnnotationReader is always used. There is still the simple reader though. --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 3 --- .../Tools/Export/AbstractClassMetadataExporterTest.php | 8 ++++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 22fa6d25f..62efbac85 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -304,9 +304,6 @@ public function () */ public function setAnnotationPrefix($prefix) { - if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) { - return; - } $this->_annotationsPrefix = $prefix; } diff --git a/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php b/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php index c9bfdccf0..2571a1b98 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Export/AbstractClassMetadataExporterTest.php @@ -98,6 +98,8 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest public function testExportDirectoryAndFilesAreCreated() { + $this->_deleteDirectory(__DIR__ . '/export/'.$this->_getType()); + $type = $this->_getType(); $metadataDriver = $this->_createMetadataDriver($type, __DIR__ . '/' . $type); $em = $this->_createEntityManager($metadataDriver); @@ -113,6 +115,7 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest $exporter = $cme->getExporter($type, __DIR__ . '/export/' . $type); if ($type === 'annotation') { $entityGenerator = new EntityGenerator(); + $entityGenerator->setAnnotationPrefix(""); $exporter->setEntityGenerator($entityGenerator); } $this->_extension = $exporter->getExtension(); @@ -139,6 +142,8 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest $cmf = $this->_createClassMetadataFactory($em, $type); $metadata = $cmf->getAllMetadata(); + $this->assertEquals(1, count($metadata)); + $class = current($metadata); $this->assertEquals('Doctrine\Tests\ORM\Tools\Export\ExportedUser', $class->name); @@ -322,8 +327,7 @@ abstract class AbstractClassMetadataExporterTest extends \Doctrine\Tests\OrmTest public function __destruct() { - $type = $this->_getType(); - $this->_deleteDirectory(__DIR__ . '/export/'.$this->_getType()); +# $this->_deleteDirectory(__DIR__ . '/export/'.$this->_getType()); } protected function _deleteDirectory($path) From 5c78ecaca1d5afe064b6774925528bf228c30e52 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 15 Oct 2011 20:44:25 +0200 Subject: [PATCH 30/42] Fix tests in EntityGenerator due to Annotation prefixes --- tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php index fce7d4c20..f2b298159 100644 --- a/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/EntityGeneratorTest.php @@ -21,6 +21,7 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase $this->_tmpDir = \sys_get_temp_dir(); \mkdir($this->_tmpDir . \DIRECTORY_SEPARATOR . $this->_namespace); $this->_generator = new EntityGenerator(); + $this->_generator->setAnnotationPrefix(""); $this->_generator->setGenerateAnnotations(true); $this->_generator->setGenerateStubMethods(true); $this->_generator->setRegenerateEntityIfExists(false); @@ -179,14 +180,15 @@ class EntityGeneratorTest extends \Doctrine\Tests\OrmTestCase public function testLoadPrefixedMetadata() { - $this->_generator->setAnnotationPrefix('orm:'); + $this->_generator->setAnnotationPrefix('ORM\\'); $metadata = $this->generateBookEntityFixture(); + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $driver = new \Doctrine\ORM\Mapping\Driver\AnnotationDriver($reader, array()); $book = $this->newInstance($metadata); $cm = new \Doctrine\ORM\Mapping\ClassMetadata($metadata->name); - $driver = $this->createAnnotationDriver(array(), 'orm'); $driver->loadMetadataForClass($cm->name, $cm); $this->assertEquals($cm->columnNames, $metadata->columnNames); From 4474d305cb89f2b12e7df2cf310065732fc8e50e Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 15 Oct 2011 21:47:16 +0200 Subject: [PATCH 31/42] DDC-1210 - Optimize UnitOfWork collection handling internally. --- lib/Doctrine/ORM/UnitOfWork.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index bd48373a4..6851d25e4 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -490,8 +490,9 @@ class UnitOfWork implements PropertyChangedListener } } else if ($orgValue instanceof PersistentCollection && $orgValue !== $actualValue) { // A PersistentCollection was de-referenced, so delete it. - if ( ! in_array($orgValue, $this->collectionDeletions, true)) { - $this->collectionDeletions[] = $orgValue; + $coid = spl_object_hash($orgValue); + if ( ! isset($this->collectionDeletions[$coid]) ) { + $this->collectionDeletions[$coid] = $orgValue; $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored. } } @@ -569,10 +570,11 @@ class UnitOfWork implements PropertyChangedListener private function computeAssociationChanges($assoc, $value) { if ($value instanceof PersistentCollection && $value->isDirty()) { + $coid = spl_object_hash($value); if ($assoc['isOwningSide']) { - $this->collectionUpdates[] = $value; + $this->collectionUpdates[$coid] = $value; } - $this->visitedCollections[] = $value; + $this->visitedCollections[$coid] = $value; } // Look through the entities, and in any of their associations, for transient (new) @@ -1889,12 +1891,12 @@ class UnitOfWork implements PropertyChangedListener { //TODO: if $coll is already scheduled for recreation ... what to do? // Just remove $coll from the scheduled recreations? - $this->collectionDeletions[] = $coll; + $this->collectionDeletions[spl_object_hash($coll)] = $coll; } public function isCollectionScheduledForDeletion(PersistentCollection $coll) { - return in_array($coll, $this->collectionsDeletions, true); + return isset( $this->collectionsDeletions[spl_object_hash($coll)] ); } /** From 33bcf7ad6f67b8641983f51901d0e74cfde8446c Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Sun, 16 Oct 2011 01:42:36 -0200 Subject: [PATCH 32/42] Added coverage to DDC-1161. --- .../Tests/ORM/Query/SelectSqlGenerationTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 8e1585819..53002a8fc 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1239,6 +1239,18 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase array(Query::HINT_FORCE_PARTIAL_LOAD => true) ); } + + /** + * @group DDC-1161 + */ + public function testSelfReferenceWithOneToOneDoesNotDuplicateAlias() + { + $this->assertSqlGeneration( + 'SELECT p, pp FROM Doctrine\Tests\Models\Company\CompanyPerson p JOIN p.spouse pp', + "SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c1_.car_id AS car_id3, c2_.salary AS salary4, c2_.department AS department5, c2_.startDate AS startDate6, c3_.id AS id7, c3_.name AS name8, c4_.title AS title9, c4_.car_id AS car_id10, c5_.salary AS salary11, c5_.department AS department12, c5_.startDate AS startDate13, c0_.discr AS discr14, c0_.spouse_id AS spouse_id15, c3_.discr AS discr16, c3_.spouse_id AS spouse_id17 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ ON c0_.spouse_id = c3_.id LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id", + array(Query::HINT_FORCE_PARTIAL_LOAD => false) + ); + } } From eeba947ea7f0f5f18672c368398f105a93e58545 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Sun, 16 Oct 2011 02:10:59 -0200 Subject: [PATCH 33/42] Code optimizations. Fixed unused argument in OrmTestCase as referred in DDC-766. --- lib/Doctrine/ORM/Query/Parser.php | 27 ++++++++++++--------------- tests/Doctrine/Tests/OrmTestCase.php | 26 +++++++++++++++----------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 08e036c1a..9fc30bb8c 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1328,18 +1328,18 @@ class Parser // We need to check if we are in a IdentificationVariable or SingleValuedPathExpression $glimpse = $this->_lexer->glimpse(); - if ($glimpse['type'] != Lexer::T_DOT) { - $token = $this->_lexer->lookahead; - $identVariable = $this->IdentificationVariable(); + if ($glimpse['type'] == Lexer::T_DOT) { + return $this->SingleValuedPathExpression(); + } + + $token = $this->_lexer->lookahead; + $identVariable = $this->IdentificationVariable(); - if (!isset($this->_queryComponents[$identVariable])) { - $this->semanticalError('Cannot group by undefined identification variable.'); - } - - return $identVariable; + if (!isset($this->_queryComponents[$identVariable])) { + $this->semanticalError('Cannot group by undefined identification variable.'); } - return $this->SingleValuedPathExpression(); + return $identVariable; } /** @@ -1354,12 +1354,9 @@ class Parser // We need to check if we are in a ResultVariable or StateFieldPathExpression $glimpse = $this->_lexer->glimpse(); - if ($glimpse['type'] != Lexer::T_DOT) { - $token = $this->_lexer->lookahead; - $expr = $this->ResultVariable(); - } else { - $expr = $this->SingleValuedPathExpression(); - } + $expr = ($glimpse['type'] != Lexer::T_DOT) + ? $this->ResultVariable() + : $this->SingleValuedPathExpression(); $item = new AST\OrderByItem($expr); diff --git a/tests/Doctrine/Tests/OrmTestCase.php b/tests/Doctrine/Tests/OrmTestCase.php index 678478633..fa9938d09 100644 --- a/tests/Doctrine/Tests/OrmTestCase.php +++ b/tests/Doctrine/Tests/OrmTestCase.php @@ -11,6 +11,7 @@ abstract class OrmTestCase extends DoctrineTestCase { /** The metadata cache that is shared between all ORM tests (except functional tests). */ private static $_metadataCacheImpl = null; + /** The query cache that is shared between all ORM tests (except functional tests). */ private static $_queryCacheImpl = null; @@ -66,30 +67,31 @@ abstract class OrmTestCase extends DoctrineTestCase */ protected function _getTestEntityManager($conn = null, $conf = null, $eventManager = null, $withSharedMetadata = true) { + $metadataCache = $withSharedMetadata + ? self::getSharedMetadataCacheImpl() + : new \Doctrine\Common\Cache\ArrayCache; + $config = new \Doctrine\ORM\Configuration(); - if($withSharedMetadata) { - $config->setMetadataCacheImpl(self::getSharedMetadataCacheImpl()); - } else { - $config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache); - } - + + $config->setMetadataCacheImpl($metadataCache); $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver()); - $config->setQueryCacheImpl(self::getSharedQueryCacheImpl()); $config->setProxyDir(__DIR__ . '/Proxies'); $config->setProxyNamespace('Doctrine\Tests\Proxies'); - $eventManager = new \Doctrine\Common\EventManager(); + if ($conn === null) { $conn = array( - 'driverClass' => 'Doctrine\Tests\Mocks\DriverMock', + 'driverClass' => 'Doctrine\Tests\Mocks\DriverMock', 'wrapperClass' => 'Doctrine\Tests\Mocks\ConnectionMock', - 'user' => 'john', - 'password' => 'wayne' + 'user' => 'john', + 'password' => 'wayne' ); } + if (is_array($conn)) { $conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, $eventManager); } + return \Doctrine\Tests\Mocks\EntityManagerMock::create($conn, $config, $eventManager); } @@ -98,6 +100,7 @@ abstract class OrmTestCase extends DoctrineTestCase if (self::$_metadataCacheImpl === null) { self::$_metadataCacheImpl = new \Doctrine\Common\Cache\ArrayCache; } + return self::$_metadataCacheImpl; } @@ -106,6 +109,7 @@ abstract class OrmTestCase extends DoctrineTestCase if (self::$_queryCacheImpl === null) { self::$_queryCacheImpl = new \Doctrine\Common\Cache\ArrayCache; } + return self::$_queryCacheImpl; } } From 0252d55c678dff09050de0e7a4f44e4aeb5cfd97 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 16 Oct 2011 11:08:11 +0200 Subject: [PATCH 34/42] DDC-1358 - Fix bug where multiple NULL root entity combined with scalar results will break the object and array hydrator. This case likeli only occurs when doing native queries. A guard clause that prevents hydration from breaking when RIGHT JOIN queries with null root entities appear has been added aswell. --- .../Internal/Hydration/AbstractHydrator.php | 5 + .../ORM/Internal/Hydration/ArrayHydrator.php | 16 ++ .../ORM/Internal/Hydration/ObjectHydrator.php | 18 ++ .../Tests/ORM/Hydration/ArrayHydratorTest.php | 54 ++++++ .../ORM/Hydration/ObjectHydratorTest.php | 161 ++++++++++++++++++ 5 files changed, 254 insertions(+) diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 181854e36..5899a69ca 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -164,6 +164,11 @@ abstract class AbstractHydrator * field names during this procedure as well as any necessary conversions on * the values applied. * + * @param array $data SQL Result Row + * @param array &$cache Cache for column to field result information + * @param array &$id Dql-Alias => ID-Hash + * @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value? + * * @return array An array with all the fields (name => value) of the data row, * grouped by their component alias. */ diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index 92eb45c5c..4b1c21c6f 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -92,6 +92,11 @@ class ArrayHydrator extends AbstractHydrator $parent = $this->_rsm->parentAliasMap[$dqlAlias]; $path = $parent . '.' . $dqlAlias; + // missing parent data, skipping as RIGHT JOIN hydration is not supported. + if ( ! isset($nonemptyComponents[$parent]) ) { + continue; + } + // Get a reference to the right element in the result tree. // This element will get the associated element attached. if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) { @@ -154,6 +159,17 @@ class ArrayHydrator extends AbstractHydrator // It's a root result element $this->_rootAliases[$dqlAlias] = true; // Mark as root + + // if this row has a NULL value for the root result id then make it a null result. + if ( ! isset($nonemptyComponents[$dqlAlias]) ) { + if ($this->_rsm->isMixed) { + $result[] = array(0 => null); + } else { + $result[] = null; + } + ++$this->_resultCounter; + continue; + } // Check for an existing element if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index bb11a7431..1287a138b 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -302,6 +302,12 @@ class ObjectHydrator extends AbstractHydrator // seen for this parent-child relationship $path = $parentAlias . '.' . $dqlAlias; + // We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs + if (!isset($nonemptyComponents[$parentAlias])) { + // TODO: Add special case code where we hydrate the right join objects into identity map at least + continue; + } + // Get a reference to the parent object to which the joined element belongs. if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) { $first = reset($this->_resultPointers); @@ -408,6 +414,18 @@ class ObjectHydrator extends AbstractHydrator // PATH C: Its a root result element $this->_rootAliases[$dqlAlias] = true; // Mark as root alias + // if this row has a NULL value for the root result id then make it a null result. + if ( ! isset($nonemptyComponents[$dqlAlias]) ) { + if ($this->_rsm->isMixed) { + $result[] = array(0 => null); + } else { + $result[] = null; + } + ++$this->_resultCounter; + continue; + } + + // check for existing result from the iterations before if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { $element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias); if (isset($this->_rsm->indexByMap[$dqlAlias])) { diff --git a/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php index 4fb5b92ac..a318f78af 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php @@ -763,4 +763,58 @@ class ArrayHydratorTest extends HydrationTestCase $this->assertEquals(1, count($result)); } + + /** + * @group DDC-1358 + */ + public function testMissingIdForRootEntity() + { + $rsm = new ResultSetMapping; + $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); + $rsm->addFieldResult('u', 'u__id', 'id'); + $rsm->addFieldResult('u', 'u__status', 'status'); + $rsm->addScalarResult('sclr0', 'nameUpper'); + + // Faked result set + $resultSet = array( + //row1 + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'sclr0' => 'ROMANB', + ), + array( + 'u__id' => null, + 'u__status' => null, + 'sclr0' => 'ROMANB', + ), + array( + 'u__id' => '2', + 'u__status' => 'developer', + 'sclr0' => 'JWAGE', + ), + array( + 'u__id' => null, + 'u__status' => null, + 'sclr0' => 'JWAGE', + ), + ); + + $stmt = new HydratorMockStatement($resultSet); + $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); + + $result = $hydrator->hydrateAll($stmt, $rsm); + + $this->assertEquals(4, count($result), "Should hydrate four results."); + + $this->assertEquals('ROMANB', $result[0]['nameUpper']); + $this->assertEquals('ROMANB', $result[1]['nameUpper']); + $this->assertEquals('JWAGE', $result[2]['nameUpper']); + $this->assertEquals('JWAGE', $result[3]['nameUpper']); + + $this->assertEquals(array('id' => 1, 'status' => 'developer'), $result[0][0]); + $this->assertNull($result[1][0]); + $this->assertEquals(array('id' => 2, 'status' => 'developer'), $result[2][0]); + $this->assertNull($result[3][0]); + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php index f2673ac70..581a3504a 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php @@ -1008,4 +1008,165 @@ class ObjectHydratorTest extends HydrationTestCase $this->assertEquals(4, count($result[1]->groups)); $this->assertEquals(2, count($result[1]->phonenumbers)); } + + /** + * @group DDC-1358 + */ + public function testMissingIdForRootEntity() + { + $rsm = new ResultSetMapping; + $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); + $rsm->addFieldResult('u', 'u__id', 'id'); + $rsm->addFieldResult('u', 'u__status', 'status'); + $rsm->addScalarResult('sclr0', 'nameUpper'); + + // Faked result set + $resultSet = array( + //row1 + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'sclr0' => 'ROMANB', + ), + array( + 'u__id' => null, + 'u__status' => null, + 'sclr0' => 'ROMANB', + ), + array( + 'u__id' => '2', + 'u__status' => 'developer', + 'sclr0' => 'JWAGE', + ), + array( + 'u__id' => null, + 'u__status' => null, + 'sclr0' => 'JWAGE', + ), + ); + + $stmt = new HydratorMockStatement($resultSet); + $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); + + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); + + $this->assertEquals(4, count($result), "Should hydrate four results."); + + $this->assertEquals('ROMANB', $result[0]['nameUpper']); + $this->assertEquals('ROMANB', $result[1]['nameUpper']); + $this->assertEquals('JWAGE', $result[2]['nameUpper']); + $this->assertEquals('JWAGE', $result[3]['nameUpper']); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0][0]); + $this->assertNull($result[1][0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[2][0]); + $this->assertNull($result[3][0]); + } + + /** + * @group DDC-1358 + * @return void + */ + public function testMissingIdForCollectionValuedChildEntity() + { + $rsm = new ResultSetMapping; + $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); + $rsm->addJoinedEntityResult( + 'Doctrine\Tests\Models\CMS\CmsPhonenumber', + 'p', + 'u', + 'phonenumbers' + ); + $rsm->addFieldResult('u', 'u__id', 'id'); + $rsm->addFieldResult('u', 'u__status', 'status'); + $rsm->addScalarResult('sclr0', 'nameUpper'); + $rsm->addFieldResult('p', 'p__phonenumber', 'phonenumber'); + + // Faked result set + $resultSet = array( + //row1 + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'sclr0' => 'ROMANB', + 'p__phonenumber' => '42', + ), + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'sclr0' => 'ROMANB', + 'p__phonenumber' => null + ), + array( + 'u__id' => '2', + 'u__status' => 'developer', + 'sclr0' => 'JWAGE', + 'p__phonenumber' => '91' + ), + array( + 'u__id' => '2', + 'u__status' => 'developer', + 'sclr0' => 'JWAGE', + 'p__phonenumber' => null + ) + ); + + $stmt = new HydratorMockStatement($resultSet); + $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); + + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); + + $this->assertEquals(2, count($result)); + $this->assertEquals(1, $result[0][0]->phonenumbers->count()); + $this->assertEquals(1, $result[1][0]->phonenumbers->count()); + } + + /** + * @group DDC-1358 + */ + public function testMissingIdForSingleValuedChildEntity() + { + $rsm = new ResultSetMapping; + $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); + $rsm->addJoinedEntityResult( + 'Doctrine\Tests\Models\CMS\CmsAddress', + 'a', + 'u', + 'address' + ); + $rsm->addFieldResult('u', 'u__id', 'id'); + $rsm->addFieldResult('u', 'u__status', 'status'); + $rsm->addScalarResult('sclr0', 'nameUpper'); + $rsm->addFieldResult('a', 'a__id', 'id'); + $rsm->addFieldResult('a', 'a__city', 'city'); + $rsm->addMetaResult('a', 'user_id', 'user_id'); + + // Faked result set + $resultSet = array( + //row1 + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'sclr0' => 'ROMANB', + 'a__id' => 1, + 'a__city' => 'Berlin', + ), + array( + 'u__id' => '2', + 'u__status' => 'developer', + 'sclr0' => 'BENJAMIN', + 'a__id' => null, + 'a__city' => null, + ), + ); + + $stmt = new HydratorMockStatement($resultSet); + $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); + + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); + + $this->assertEquals(2, count($result)); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $result[0][0]->address); + $this->assertNull($result[1][0]->address); + } } From a8e6131e3bc883997b16133e65ee44ca9c59d8db Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Sun, 16 Oct 2011 17:00:33 +0200 Subject: [PATCH 35/42] Added the initializeObject method in the EntityManager --- lib/Doctrine/ORM/EntityManager.php | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 04a30dc46..d8deb0589 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -128,7 +128,7 @@ class EntityManager implements ObjectManager $this->metadataFactory = new $metadataFactoryClassName; $this->metadataFactory->setEntityManager($this); $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl()); - + $this->unitOfWork = new UnitOfWork($this); $this->proxyFactory = new ProxyFactory($this, $config->getProxyDir(), @@ -203,18 +203,18 @@ class EntityManager implements ObjectManager public function transactional(Closure $func) { $this->conn->beginTransaction(); - + try { $return = $func($this); - + $this->flush(); $this->conn->commit(); - + return $return ?: true; } catch (Exception $e) { $this->close(); $this->conn->rollback(); - + throw $e; } } @@ -244,7 +244,7 @@ class EntityManager implements ObjectManager * * The class name must be the fully-qualified class name without a leading backslash * (as it is returned by get_class($obj)) or an aliased class name. - * + * * Examples: * MyProject\Domain\User * sales:PriceRequest @@ -450,7 +450,7 @@ class EntityManager implements ObjectManager * * The entity will be entered into the database at or before transaction * commit or as a result of the flush operation. - * + * * NOTE: The persist operation always considers entities that are not yet known to * this EntityManager as NEW. Do not pass detached entities to the persist operation. * @@ -633,7 +633,7 @@ class EntityManager implements ObjectManager /** * Check if the Entity manager is open or closed. - * + * * @return bool */ public function isOpen() @@ -714,6 +714,18 @@ class EntityManager implements ObjectManager return $this->proxyFactory; } + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * This method is a no-op for other objects + * + * @param object $obj + */ + public function initializeObject($obj) + { + $this->unitOfWork->initializeObject($obj); + } + /** * Factory method to create EntityManager instances. * From 939fbf9c24f45ea01491bf1dd72921994c7f1656 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 16 Oct 2011 22:45:06 +0200 Subject: [PATCH 36/42] 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)); } } From 91bc9c0329aa7d8dfa76f07acb9c9038d2c80986 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 17 Oct 2011 18:54:20 +0200 Subject: [PATCH 37/42] Adjusted test to verify that findBy*(null) is now supported --- tests/Doctrine/Tests/Models/CMS/CmsUser.php | 2 +- .../ORM/Functional/EntityRepositoryTest.php | 32 ++++++++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUser.php b/tests/Doctrine/Tests/Models/CMS/CmsUser.php index 0c7b007d1..3296fffc8 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUser.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUser.php @@ -19,7 +19,7 @@ class CmsUser */ public $id; /** - * @Column(type="string", length=50) + * @Column(type="string", length=50, nullable=true) */ public $status; /** diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index c5216fb94..7cd37b906 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -38,18 +38,25 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $user2->status = 'dev'; $this->_em->persist($user2); + $user3 = new CmsUser; + $user3->name = 'Benjamin'; + $user3->username = 'beberlei'; + $user3->status = null; + $this->_em->persist($user3); + $this->_em->flush(); - + $user1Id = $user->getId(); - + unset($user); unset($user2); - + unset($user3); + $this->_em->clear(); return $user1Id; } - + public function loadAssociatedFixture() { $address = new CmsAddress(); @@ -189,7 +196,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $users = $repos->findAll(); - $this->assertEquals(2, count($users)); + $this->assertEquals(3, count($users)); } public function testFindByAlias() @@ -202,7 +209,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $repos = $this->_em->getRepository('CMS:CmsUser'); $users = $repos->findAll(); - $this->assertEquals(2, count($users)); + $this->assertEquals(3, count($users)); } /** @@ -284,10 +291,11 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testFindMagicCallByNullValue() { $this->loadFixture(); + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); - $this->setExpectedException('Doctrine\ORM\ORMException'); $users = $repos->findByStatus(null); + $this->assertEquals(1, count($users)); } /** @@ -411,7 +419,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $users1 = $repos->findBy(array(), null, 1, 0); $users2 = $repos->findBy(array(), null, 1, 1); - $this->assertEquals(2, count($repos->findBy(array()))); + $this->assertEquals(3, count($repos->findBy(array()))); $this->assertEquals(1, count($users1)); $this->assertEquals(1, count($users2)); $this->assertNotSame($users1[0], $users2[0]); @@ -428,10 +436,10 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $usersAsc = $repos->findBy(array(), array("username" => "ASC")); $usersDesc = $repos->findBy(array(), array("username" => "DESC")); - $this->assertEquals(2, count($usersAsc), "Pre-condition: only two users in fixture"); - $this->assertEquals(2, count($usersDesc), "Pre-condition: only two users in fixture"); - $this->assertSame($usersAsc[0], $usersDesc[1]); - $this->assertSame($usersAsc[1], $usersDesc[0]); + $this->assertEquals(3, count($usersAsc), "Pre-condition: only three users in fixture"); + $this->assertEquals(3, count($usersDesc), "Pre-condition: only three users in fixture"); + $this->assertSame($usersAsc[0], $usersDesc[2]); + $this->assertSame($usersAsc[2], $usersDesc[0]); } From b8af2415042dd5e3ab8518b8a3ab8775c8dab56a Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 17 Oct 2011 20:53:04 +0200 Subject: [PATCH 38/42] Added a testcase for findBy(.. => null) and renamed 'old' testcase --- .../Tests/ORM/Functional/EntityRepositoryTest.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index 7cd37b906..4f8e11420 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -397,7 +397,7 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase /** * @group DDC-1087 */ - public function testIsNullCriteria() + public function testIsNullCriteriaDoesNotGenerateAParameter() { $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); $users = $repos->findBy(array('status' => null, 'username' => 'romanb')); @@ -407,6 +407,16 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(array('romanb'), $params); } + public function testIsNullCriteria() + { + $this->loadFixture(); + + $repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser'); + + $users = $repos->findBy(array('status' => null)); + $this->assertEquals(1, count($users)); + } + /** * @group DDC-1094 */ From c3ec6e383c87b6f81dc74bbd9e6799230ece01de Mon Sep 17 00:00:00 2001 From: Sergey Linnik Date: Tue, 18 Oct 2011 01:14:07 +0400 Subject: [PATCH 39/42] Fix isTransient call on uninitialized ClassMetadataFactory --- lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 248e4a2f9..239022b0f 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -494,6 +494,10 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface */ public function isTransient($class) { + if ( ! $this->initialized) { + $this->initialize(); + } + return $this->driver->isTransient($class); } } From 0d57ffbc3be10eacdb77ed23825a23ce576f3b85 Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Tue, 18 Oct 2011 15:48:56 +0200 Subject: [PATCH 40/42] Set association-key attribute in xml mapping --- lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php index f9668c78f..01309475b 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/XmlExporter.php @@ -139,6 +139,9 @@ class XmlExporter extends AbstractExporter if (isset($field['columnName'])) { $idXml->addAttribute('column', $field['columnName']); } + if (isset($field['associationKey']) && $field['associationKey']) { + $idXml->addAttribute('association-key', 'true'); + } if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) { $generatorXml = $idXml->addChild('generator'); $generatorXml->addAttribute('strategy', $idGeneratorType); From ca01065c6ad41eff9b5524522dc3686cf9e8709a Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 19 Oct 2011 11:58:59 +0200 Subject: [PATCH 41/42] Bugfix in short identifier shortcut with association ids --- lib/Doctrine/ORM/Proxy/ProxyFactory.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index 05a44d992..490c3a119 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -230,10 +230,12 @@ class ProxyFactory */ private function isShortIdentifierGetter($method, $class) { + $identifier = lcfirst(substr($method->getName(), 3)); return ( $method->getNumberOfParameters() == 0 && substr($method->getName(), 0, 3) == "get" && - in_array(lcfirst(substr($method->getName(), 3)), $class->identifier, true) && + in_array($identifier, $class->identifier, true) && + $class->hasField($identifier) && (($method->getEndLine() - $method->getStartLine()) <= 4) ); } From 4a50eb4fa749cfbb59552dfa0abc56323c6930a9 Mon Sep 17 00:00:00 2001 From: armetiz Date: Fri, 21 Oct 2011 11:55:54 +0300 Subject: [PATCH 42/42] Update lib/Doctrine/ORM/Query/Expr.php --- lib/Doctrine/ORM/Query/Expr.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Expr.php b/lib/Doctrine/ORM/Query/Expr.php index 246f92717..6ba6a5d73 100644 --- a/lib/Doctrine/ORM/Query/Expr.php +++ b/lib/Doctrine/ORM/Query/Expr.php @@ -40,10 +40,12 @@ class Expr * * [php] * // (u.type = ?1) AND (u.role = ?2) - * $expr->andX('u.type = ?1', 'u.role = ?2')); + * $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2')); * - * @param mixed $x Optional clause. Defaults = null, but requires - * at least one defined when converting to string. + * @param Doctrine\ORM\Query\Expr\Comparison | + * Doctrine\ORM\Query\Expr\Func | + * Doctrine\ORM\Query\Expr\Orx + * $x Optional clause. Defaults = null, but requires at least one defined when converting to string. * @return Expr\Andx */ public function andX($x = null)