From 25b7c64dc6b6a1881546c4c46d2cfca4b6c58d96 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Tue, 13 Jan 2015 17:50:20 +0000 Subject: [PATCH] Implemented support for extra lazy get for both owning and inverse side on many to many associations. --- lib/Doctrine/ORM/PersistentCollection.php | 1 - .../ORM/Persisters/BasicEntityPersister.php | 45 ++++++++++++++++-- .../ORM/Persisters/ManyToManyPersister.php | 20 +++++++- .../ORM/Persisters/OneToManyPersister.php | 26 +++++----- .../Functional/ExtraLazyCollectionTest.php | 47 +++++++++++++++++-- 5 files changed, 115 insertions(+), 24 deletions(-) diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index c668d7a51..d0a6d2c6c 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -524,7 +524,6 @@ final class PersistentCollection implements Collection, Selectable public function get($key) { if ( ! $this->initialized - && $this->association['type'] === Mapping\ClassMetadataInfo::ONE_TO_MANY && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY && isset($this->association['indexBy']) ) { diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index e289d540d..a4bc9cd9a 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -1621,14 +1621,29 @@ class BasicEntityPersister implements EntityPersister } if (isset($this->class->associationMappings[$field])) { + $association = $this->class->associationMappings[$field]; - if ( ! $this->class->associationMappings[$field]['isOwningSide']) { + // Many-To-Many requires join table check for joinColumn + if ($association['type'] === ClassMetadata::MANY_TO_MANY) { + if ( ! $association['isOwningSide']) { + $association = $assoc; + } + + $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); + $joinColumn = $assoc['isOwningSide'] + ? $association['joinTable']['joinColumns'][0] + : $association['joinTable']['inverseJoinColumns'][0]; + + return $joinTableName . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); + } + + if ( ! $association['isOwningSide']) { throw ORMException::invalidFindByInverseAssociation($this->class->name, $field); } - $joinColumn = $this->class->associationMappings[$field]['joinColumns'][0]; - $className = (isset($this->class->associationMappings[$field]['inherited'])) - ? $this->class->associationMappings[$field]['inherited'] + $joinColumn = $association['joinColumns'][0]; + $className = (isset($association['inherited'])) + ? $association['inherited'] : $this->class->name; return $this->getSQLTableAlias($className) . '.' . $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform); @@ -1767,6 +1782,28 @@ class BasicEntityPersister implements EntityPersister $type = $this->class->fieldMappings[$field]['type']; break; + case (isset($this->class->associationMappings[$field]) && $this->class->associationMappings[$field]['type'] === ClassMetadata::MANY_TO_MANY): + $assoc = $this->class->associationMappings[$field]; + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + + if ( ! $assoc['isOwningSide']) { + $assoc = $targetClass->associationMappings[$assoc['mappedBy']]; + } + + if (count($assoc['relationToTargetKeyColumns']) > 1) { + throw Query\QueryException::associationPathCompositeKeyNotSupported(); + } + + $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); + $targetColumn = $assoc['joinTable']['inverseJoinColumns'][0]['referencedColumnName']; + $type = null; + + if (isset($targetClass->fieldNames[$targetColumn])) { + $type = $targetClass->fieldMappings[$targetClass->fieldNames[$targetColumn]]['type']; + } + + break; + case (isset($this->class->associationMappings[$field])): $assoc = $this->class->associationMappings[$field]; diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index 1cb9f8f74..e09d28b40 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -77,7 +77,19 @@ class ManyToManyPersister extends AbstractCollectionPersister */ public function get(PersistentCollection $coll, $index) { - throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister."); + $mapping = $coll->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + if ( ! $mapping['isOwningSide']) { + return $persister->load(array($mapping['mappedBy'] => $coll->getOwner(), $mapping['indexBy'] => $index), null, $mapping, array(), 0, 1); + } + + return $persister->load(array($mapping['inversedBy'] => $coll->getOwner(), $mapping['indexBy'] => $index), null, $mapping, array(), 0, 1); } /** @@ -138,6 +150,12 @@ class ManyToManyPersister extends AbstractCollectionPersister */ public function containsKey(PersistentCollection $coll, $key) { + $mapping = $coll->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictionsWithKey($coll, $key, true); $sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses); diff --git a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php index 0e3f05642..9d2e1a4a6 100644 --- a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -60,24 +60,15 @@ class OneToManyPersister extends AbstractCollectionPersister */ public function get(PersistentCollection $coll, $index) { - $mapping = $coll->getMapping(); - $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + $mapping = $coll->getMapping(); - if (!isset($mapping['indexBy'])) { + if ( ! isset($mapping['indexBy'])) { throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); } - return $persister->load( - array( - $mapping['mappedBy'] => $coll->getOwner(), - $mapping['indexBy'] => $index - ), - null, - null, - array(), - null, - 1 - ); + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); + + return $persister->load(array($mapping['mappedBy'] => $coll->getOwner(), $mapping['indexBy'] => $index), null, $mapping, array(), null, 1); } /** @@ -112,7 +103,12 @@ class OneToManyPersister extends AbstractCollectionPersister */ public function containsKey(PersistentCollection $coll, $key) { - $mapping = $coll->getMapping(); + $mapping = $coll->getMapping(); + + if ( ! isset($mapping['indexBy'])) { + throw new \BadMethodCallException("Selecting a collection by index is only supported on indexed collections."); + } + $persister = $this->uow->getEntityPersister($mapping['targetEntity']); // only works with single id identifier entities. Will throw an diff --git a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php index e724517e8..bdb05f76b 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php @@ -20,6 +20,8 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase private $ddc2504OtherClassId; private $ddc2504ChildClassId; + private $username; + private $groupname; private $topic; private $phonenumber; @@ -59,6 +61,8 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup'); $class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_LAZY; + + unset($class->associationMappings['users']['indexBy']); } /** @@ -754,6 +758,40 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertSame($article, $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId)); } + /** + * @group DDC-1398 + */ + public function testGetIndexByManyToManyInverseSide() + { + $group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId); + /* @var $group CmsGroup */ + + $queryCount = $this->getCurrentQueryCount(); + + $user = $group->users->get($this->username); + + $this->assertFalse($group->users->isInitialized()); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertSame($user, $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId)); + } + + /** + * @group DDC-1398 + */ + public function testGetIndexByManyToManyOwningSide() + { + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + /* @var $user CmsUser */ + + $queryCount = $this->getCurrentQueryCount(); + + $group = $user->groups->get($this->groupname); + + $this->assertFalse($user->groups->isInitialized()); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + $this->assertSame($group, $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId)); + } + /** * @group DDC-1398 */ @@ -761,6 +799,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase { $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); $this->assertNull($user->articles->get(-1)); + $this->assertNull($user->groups->get(-1)); } public function testContainsKeyIndexByOneToMany() @@ -973,11 +1012,13 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->userId = $user1->getId(); $this->userId2 = $user2->getId(); $this->groupId = $group1->id; - - $this->topic = $article1->topic; - $this->phonenumber = $phonenumber1->phonenumber; $this->ddc2504OtherClassId = $otherClass->id; $this->ddc2504ChildClassId = $childClass1->id; + $this->username = $user1->username; + $this->groupname = $group1->name; + $this->topic = $article1->topic; + $this->phonenumber = $phonenumber1->phonenumber; + } }