diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index b56d0930b..fc2a00ba1 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -404,22 +404,12 @@ final class PersistentCollection implements Collection */ public function contains($element) { - /* DRAFT - if ($this->initialized) { - return $this->coll->contains($element); - } else { - if ($element is MANAGED) { - if ($this->coll->contains($element)) { - return true; - } - $exists = check db for existence; - if ($exists) { - $this->coll->add($element); - } - return $exists; - } - return false; - }*/ + if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRALAZY) { + return $this->coll->contains($element) || + $this->em->getUnitOfWork() + ->getCollectionPersister($this->association) + ->contains($this, $element); + } $this->initialize(); return $this->coll->contains($element); @@ -481,7 +471,7 @@ final class PersistentCollection implements Collection if (!isset($this->doctrineCollectionCount)) { $this->doctrineCollectionCount = $this->em->getUnitOfWork() ->getCollectionPersister($this->association) - ->count($this) + count ($this->coll->toArray()); + ->count($this) + $this->coll->count(); } return $this->doctrineCollectionCount; } diff --git a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php index 1847c7136..189809697 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -135,12 +135,12 @@ abstract class AbstractCollectionPersister throw new \BadMethodCallException("Slicing elements is not supported by this CollectionPersister."); } - public function contains(PersistentCollection $coll, $key) + public function contains(PersistentCollection $coll, $element) { throw new \BadMethodCallException("Checking for existance of an element is not supported by this CollectionPersister."); } - public function containsKey(PersistentCollection $coll, $element) + public function containsKey(PersistentCollection $coll, $key) { throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister."); } diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index 9d4974eac..0eb46edcb 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -1326,21 +1326,17 @@ class BasicEntityPersister * @param object $entity * @return boolean TRUE if the entity exists in the database, FALSE otherwise. */ - public function exists($entity) + public function exists($entity, array $extraConditions = array()) { $criteria = $this->_class->getIdentifierValues($entity); + if ($extraConditions) { + $criteria = array_merge($criteria, $extraConditions); + } + $sql = 'SELECT 1 FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($this->_class->name) . ' WHERE ' . $this->_getSelectConditionSQL($criteria); return (bool) $this->_conn->fetchColumn($sql, array_values($criteria)); } - - //TODO - /*protected function _getOneToOneEagerFetchSQL() - { - - }*/ - - #public function countCollection } diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index 65afe23f0..9e3dc93b5 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -21,7 +21,8 @@ namespace Doctrine\ORM\Persisters; -use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\PersistentCollection, + Doctrine\ORM\UnitOfWork; /** * Persister for many-to-many collections. @@ -230,4 +231,53 @@ class ManyToManyPersister extends AbstractCollectionPersister ->getEntityPersister($mapping['targetEntity']) ->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length); } + + /** + * @param PersistentCollection $coll + * @param object $element + */ + public function contains(PersistentCollection $coll, $element) + { + $uow = $this->_em->getUnitOfWork(); + + // shortcut for new entities + if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) { + return false; + } + + $params = array(); + $mapping = $coll->getMapping(); + $sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']); + $elementClass = $this->_em->getClassMetadata($mapping['targetEntity']); + $sourceId = $uow->getEntityIdentifier($coll->getOwner()); + $elementId = $uow->getEntityIdentifier($element); + + if ($mapping['isOwningSide']) { + $joinTable = $mapping['joinTable']; + } else { + $joinTable = $elementClass->associationMappings[$mapping['mappedBy']]['joinTable']; + } + + $whereClause = ''; + foreach ($mapping['joinTableColumns'] as $joinTableColumn) { + if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { + if ($whereClause !== '') { + $whereClause .= ' AND '; + } + $whereClause .= "$joinTableColumn = ?"; + + $params[] = $elementId[$sourceClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; + } else if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) { + if ($whereClause !== '') { + $whereClause .= ' AND '; + } + $whereClause .= "$joinTableColumn = ?"; + + $params[] = $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; + } + } + $sql = 'SELECT 1 FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause; + + return (bool)$this->_conn->fetchColumn($sql, $params); + } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php index d60cc980f..4e17cb973 100644 --- a/lib/Doctrine/ORM/Persisters/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/OneToManyPersister.php @@ -21,7 +21,8 @@ namespace Doctrine\ORM\Persisters; -use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\PersistentCollection, + Doctrine\ORM\UnitOfWork; /** * Persister for one-to-many collections. @@ -153,4 +154,26 @@ class OneToManyPersister extends AbstractCollectionPersister ->getEntityPersister($mapping['targetEntity']) ->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length); } + + /** + * @param PersistentCollection $coll + * @param object $element + */ + public function contains(PersistentCollection $coll, $element) + { + $mapping = $coll->getMapping(); + $uow = $this->_em->getUnitOfWork(); + + // shortcut for new entities + if ($uow->getEntityState($element, UnitOfWork::STATE_NEW) == UnitOfWork::STATE_NEW) { + return false; + } + + // only works with single id identifier entities. Will throw an exception in Entity Persisters + // if that is not the case for the 'mappedBy' field. + $id = current( $uow->getEntityIdentifier($coll->getOwner()) ); + + return $uow->getEntityPersister($mapping['targetEntity']) + ->exists($element, array($mapping['mappedBy'] => $id)); + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php index fbd6451ac..c4de77eac 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php @@ -15,6 +15,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase { private $userId; private $groupId; + private $articleId; public function setUp() { @@ -215,6 +216,70 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($queryCount + 2, $this->getCurrentQueryCount()); } + /** + * @group DDC-546 + */ + public function testContainsOneToMany() + { + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + $this->assertFalse($user->articles->isInitialized(), "Pre-Condition: Collection is not initialized."); + + $article = $this->_em->find('Doctrine\Tests\Models\CMS\CmsArticle', $this->articleId); + + $queryCount = $this->getCurrentQueryCount(); + $this->assertTrue($user->articles->contains($article)); + $this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized."); + $this->assertEquals($queryCount + 1, $this->getCurrentQueryCount()); + + $article = new \Doctrine\Tests\Models\CMS\CmsArticle(); + $article->topic = "Testnew"; + $article->text = "blub"; + + $queryCount = $this->getCurrentQueryCount(); + $this->assertFalse($user->articles->contains($article)); + $this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Checking for contains of new entity should cause no query to be executed."); + + $this->_em->persist($article); + $this->_em->flush(); + + $queryCount = $this->getCurrentQueryCount(); + $this->assertFalse($user->articles->contains($article)); + $this->assertEquals($queryCount+1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed."); + $this->assertFalse($user->articles->isInitialized(), "Post-Condition: Collection is not initialized."); + } + + /** + * @group DDC-546 + */ + public function testContainsManyToMany() + { + $user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId); + $this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized."); + + $group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId); + + $queryCount = $this->getCurrentQueryCount(); + $this->assertTrue($user->groups->contains($group)); + $this->assertEquals($queryCount+1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed."); + $this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized."); + + $group = new \Doctrine\Tests\Models\CMS\CmsGroup(); + $group->name = "A New group!"; + + $queryCount = $this->getCurrentQueryCount(); + $this->assertFalse($user->groups->contains($group)); + $this->assertEquals($queryCount, $this->getCurrentQueryCount(), "Checking for contains of new entity should cause no query to be executed."); + $this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized."); + + $this->_em->persist($group); + $this->_em->flush(); + + $queryCount = $this->getCurrentQueryCount(); + $this->assertFalse($user->groups->contains($group)); + $this->assertEquals($queryCount+1, $this->getCurrentQueryCount(), "Checking for contains of managed entity should cause one query to be executed."); + $this->assertFalse($user->groups->isInitialized(), "Post-Condition: Collection is not initialized."); + } + private function loadFixture() { $user1 = new \Doctrine\Tests\Models\CMS\CmsUser(); @@ -278,7 +343,8 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->flush(); $this->_em->clear(); - + + $this->articleId = $article1->id; $this->userId = $user1->getId(); $this->groupId = $group1->id; }