1
0
mirror of synced 2025-02-20 06:03:15 +03:00

Entra-lazy for containsKey on collections

This commit is contained in:
Asmir Mustafic 2014-02-06 12:27:12 +01:00
parent 626efdafd4
commit 557686aa0a
4 changed files with 240 additions and 24 deletions

View File

@ -471,6 +471,13 @@ final class PersistentCollection implements Collection, Selectable
*/
public function containsKey($key)
{
if (! $this->initialized && $this->association['fetch'] === Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY
&& isset($this->association['indexBy'])) {
$persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
return $this->coll->containsKey($key) || $persister->containsKey($this, $key);
}
$this->initialize();
return $this->coll->containsKey($key);
@ -778,7 +785,7 @@ final class PersistentCollection implements Collection, Selectable
public function next()
{
$this->initialize();
return $this->coll->next();
}

View File

@ -265,7 +265,16 @@ class ManyToManyPersister extends AbstractCollectionPersister
return $this->em->getUnitOfWork()->getEntityPersister($mapping['targetEntity'])->getManyToManyCollection($mapping, $coll->getOwner(), $offset, $length);
}
/**
* {@inheritdoc}
*/
public function containsKey(PersistentCollection $coll, $key)
{
list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictionsWithKey($coll, $key, true);
$sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
return (bool) $this->conn->fetchColumn($sql, $params);
}
/**
* {@inheritdoc}
*/
@ -319,6 +328,75 @@ class ManyToManyPersister extends AbstractCollectionPersister
return (bool) $this->conn->executeUpdate($sql, $params);
}
/**
* @param \Doctrine\ORM\PersistentCollection $coll
* @param string $key
* @param boolean $addFilters Whether the filter SQL should be included or not.
*
* @return array
*/
private function getJoinTableRestrictionsWithKey(PersistentCollection $coll, $key, $addFilters)
{
$uow = $this->em->getUnitOfWork();
$filterMapping = $coll->getMapping();
$mapping = $filterMapping;
$indexBy = $mapping['indexBy'];
$wasOwning = $mapping['isOwningSide'];
$id = $uow->getEntityIdentifier($coll->getOwner());
$targetEntity = $this->em->getClassMetadata($mapping['targetEntity']);
if (! $mapping['isOwningSide']) {
$associationSourceClass = $this->em->getClassMetadata($mapping['targetEntity']);
$mapping = $associationSourceClass->associationMappings[$mapping['mappedBy']];
} else {
$associationSourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
}
$quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $associationSourceClass, $this->platform). ' t';
$whereClauses = array();
$params = array();
$joinNeeded = !in_array($indexBy, $targetEntity->identifier);
if ($joinNeeded) { // extra join needed if indexBy is not a @id
$joinConditions = array();
foreach ($wasOwning?$mapping['joinTable']['inverseJoinColumns']:$mapping['joinTable']['joinColumns'] as $joinTableColumn) {
$joinConditions[] = 't.'.$joinTableColumn['name'].' = tr.'.$joinTableColumn['referencedColumnName'];
}
$tableName = $this->quoteStrategy->getTableName($targetEntity, $this->platform);
$quotedJoinTable .= ' JOIN '. $tableName. ' tr ON '.implode(' AND ', $joinConditions);
$whereClauses[] = 'tr.'.$targetEntity->getColumnName($indexBy).' = ?';
$params[] = $key;
}
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
$whereClauses[] = 't.' . $joinTableColumn . ' = ?';
$params[] = $targetEntity->containsForeignIdentifier
? $id[$targetEntity->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]
: $id[$targetEntity->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
} elseif (!$joinNeeded) {
$whereClauses[] = 't.' . $joinTableColumn . ' = ?';
$params[] = $key;
}
}
if ($addFilters) {
list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping);
if ($filterSql) {
$quotedJoinTable .= ' ' . $joinTargetEntitySQL;
$whereClauses[] = $filterSql;
}
}
return array($quotedJoinTable, $whereClauses, $params);
}
/**
* @param \Doctrine\ORM\PersistentCollection $coll
* @param object $element

View File

@ -131,6 +131,45 @@ class OneToManyPersister extends AbstractCollectionPersister
* {@inheritdoc}
*/
public function count(PersistentCollection $coll)
{
list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, true);
$sql = 'SELECT count(*) FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
return $this->conn->fetchColumn($sql, $params);
}
/**
* {@inheritdoc}
*/
public function slice(PersistentCollection $coll, $offset, $length = null)
{
$mapping = $coll->getMapping();
$uow = $this->em->getUnitOfWork();
$persister = $uow->getEntityPersister($mapping['targetEntity']);
return $persister->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
}
/**
* {@inheritdoc}
*/
public function containsKey(PersistentCollection $coll, $key)
{
list($quotedJoinTable, $whereClauses, $params) = $this->getJoinTableRestrictions($coll, true);
$mapping = $coll->getMapping();
$sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']);
$whereClauses[] = $sourceClass->getColumnName($mapping['indexBy']) . ' = ?';
$params[] = $key;
$sql = 'SELECT 1 FROM ' . $quotedJoinTable . ' WHERE ' . implode(' AND ', $whereClauses);
return (bool) $this->conn->fetchColumn($sql, $params);
}
private function getJoinTableRestrictions(PersistentCollection $coll, $addFilters)
{
$mapping = $coll->getMapping();
$targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
@ -149,30 +188,18 @@ class OneToManyPersister extends AbstractCollectionPersister
: $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
}
$filterTargetClass = $this->em->getClassMetadata($targetClass->rootEntityName);
foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
if ($filterExpr = $filter->addFilterConstraint($filterTargetClass, 't')) {
$whereClauses[] = '(' . $filterExpr . ')';
if ($addFilters) {
$filterTargetClass = $this->em->getClassMetadata($targetClass->rootEntityName);
foreach ($this->em->getFilters()->getEnabledFilters() as $filter) {
if ($filterExpr = $filter->addFilterConstraint($filterTargetClass, 't')) {
$whereClauses[] = '(' . $filterExpr . ')';
}
}
}
$sql = 'SELECT count(*)'
. ' FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' t'
. ' WHERE ' . implode(' AND ', $whereClauses);
$quotedJoinTable = $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' t';
return $this->conn->fetchColumn($sql, $params);
}
/**
* {@inheritdoc}
*/
public function slice(PersistentCollection $coll, $offset, $length = null)
{
$mapping = $coll->getMapping();
$uow = $this->em->getUnitOfWork();
$persister = $uow->getEntityPersister($mapping['targetEntity']);
return $persister->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
return array($quotedJoinTable, $whereClauses, $params);
}
/**
@ -200,7 +227,7 @@ class OneToManyPersister extends AbstractCollectionPersister
// 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()));
$id = current($uow->getEntityIdentifier($coll->getOwner()));
return $persister->exists($element, array($mapping['mappedBy'] => $id));
}

View File

@ -35,6 +35,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup');
$class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
$class->associationMappings['users']['indexBy'] = 'username';
$this->loadFixture();
}
@ -539,7 +540,6 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
/* @var $user CmsUser */
$queryCount = $this->getCurrentQueryCount();
$phonenumber = $user->phonenumbers->get($this->phonenumber);
$this->assertFalse($user->phonenumbers->isInitialized());
@ -576,6 +576,106 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertNull($user->articles->get(-1));
}
public function testContainsKeyIndexByOneToMany()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
/* @var $user CmsUser */
$queryCount = $this->getCurrentQueryCount();
$contains = $user->articles->containsKey($this->topic);
$this->assertTrue($contains);
$this->assertFalse($user->articles->isInitialized());
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
}
public function testContainsKeyIndexByManyToMany()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
$queryCount = $this->getCurrentQueryCount();
$contains = $user->groups->containsKey($group->name);
$this->assertTrue($contains, "The item is not into collection");
$this->assertFalse($user->groups->isInitialized(), "The collection must not be initialized");
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
}
public function testContainsKeyIndexByManyToManyNonOwning()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
$queryCount = $this->getCurrentQueryCount();
$contains = $group->users->containsKey($user->username);
$this->assertTrue($contains, "The item is not into collection");
$this->assertFalse($group->users->isInitialized(), "The collection must not be initialized");
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
}
public function testContainsKeyIndexByWithPkManyToMany()
{
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
$class->associationMappings['groups']['indexBy'] = 'id';
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$queryCount = $this->getCurrentQueryCount();
$contains = $user->groups->containsKey($this->groupId);
$this->assertTrue($contains, "The item is not into collection");
$this->assertFalse($user->groups->isInitialized(), "The collection must not be initialized");
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
}
public function testContainsKeyIndexByWithPkManyToManyNonOwning()////
{
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup');
$class->associationMappings['users']['indexBy'] = 'id';
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
$queryCount = $this->getCurrentQueryCount();
$contains = $group->users->containsKey($this->userId);
$this->assertTrue($contains, "The item is not into collection");
$this->assertFalse($group->users->isInitialized(), "The collection must not be initialized");
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
}
public function testContainsKeyNonExistentIndexByOneToMany()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$queryCount = $this->getCurrentQueryCount();
$contains = $user->articles->containsKey("NonExistentTopic");
$this->assertFalse($contains);
$this->assertFalse($user->articles->isInitialized());
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
}
public function testContainsKeyNonExistentIndexByManyToMany()
{
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
$queryCount = $this->getCurrentQueryCount();
$contains = $user->groups->containsKey("NonExistentTopic");
$this->assertFalse($contains);
$this->assertFalse($user->groups->isInitialized());
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
}
private function loadFixture()
{
$user1 = new \Doctrine\Tests\Models\CMS\CmsUser();
@ -646,6 +746,8 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($phonenumber1);
$this->_em->persist($phonenumber2);
$user1->addPhonenumber($phonenumber1);
$this->_em->flush();
$this->_em->clear();
@ -655,5 +757,7 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->topic = $article1->topic;
$this->phonenumber = $phonenumber1->phonenumber;
}
}