1
0
Fork 0
mirror of synced 2025-04-03 13:23:37 +03:00

Merge pull request #1032 from bakura10/optimized-contains

Add support for optimized contains in LazyCriteria
This commit is contained in:
Guilherme Blanco 2014-06-21 08:56:47 -04:00
commit 38187a31d6
9 changed files with 77 additions and 24 deletions

View file

@ -2,17 +2,17 @@
## Minor BC BREAK: Custom Hydrators API change ## Minor BC BREAK: Custom Hydrators API change
As of 2.5, `AbstractHydrator` does not enforce the usage of cache as part of As of 2.5, `AbstractHydrator` does not enforce the usage of cache as part of
API, and now provides you a clean API for column information through the method API, and now provides you a clean API for column information through the method
`hydrateColumnInfo($column)`. `hydrateColumnInfo($column)`.
Cache variable being passed around by reference is no longer needed since Cache variable being passed around by reference is no longer needed since
Hydrators are per query instantiated since Doctrine 2.4. Hydrators are per query instantiated since Doctrine 2.4.
## Minor BC BREAK: Entity based ``EntityManager#clear()`` calls follow cascade detach ## Minor BC BREAK: Entity based ``EntityManager#clear()`` calls follow cascade detach
Whenever ``EntityManager#clear()`` method gets called with a given entity class Whenever ``EntityManager#clear()`` method gets called with a given entity class
name, until 2.4, it was only detaching the specific requested entity. name, until 2.4, it was only detaching the specific requested entity.
As of 2.5, ``EntityManager`` will follow configured cascades, providing a better As of 2.5, ``EntityManager`` will follow configured cascades, providing a better
memory management since associations will be garbage collected, optimizing memory management since associations will be garbage collected, optimizing
resources consumption on long running jobs. resources consumption on long running jobs.

View file

@ -191,9 +191,9 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function exists($entity, array $extraConditions = array()) public function exists($entity, Criteria $extraConditions = null)
{ {
if (empty($extraConditions)) { if (null === $extraConditions) {
$key = new EntityCacheKey($this->class->rootEntityName, $this->class->getIdentifierValues($entity)); $key = new EntityCacheKey($this->class->rootEntityName, $this->class->getIdentifierValues($entity));
if ($this->region->contains($key)) { if ($this->region->contains($key)) {

View file

@ -30,7 +30,7 @@ use Doctrine\ORM\Persisters\EntityPersister;
* A lazy collection that allow a fast count when using criteria object * A lazy collection that allow a fast count when using criteria object
* Once count gets executed once without collection being initialized, result * Once count gets executed once without collection being initialized, result
* is cached and returned on subsequent calls until collection gets loaded, * is cached and returned on subsequent calls until collection gets loaded,
* then returning the number of loaded results. * then returning the number of loaded results.
* *
* @since 2.5 * @since 2.5
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
@ -82,6 +82,21 @@ class LazyCriteriaCollection extends AbstractLazyCollection implements Selectabl
return $this->count = $this->entityPersister->count($this->criteria); return $this->count = $this->entityPersister->count($this->criteria);
} }
/**
* Do an optimized search of an element
*
* @param object $element
* @return bool
*/
public function contains($element)
{
if ($this->isInitialized()) {
return $this->collection->contains($element);
}
return $this->entityPersister->exists($element, $this->criteria);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View file

@ -1835,7 +1835,7 @@ class BasicEntityPersister implements EntityPersister
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function exists($entity, array $extraConditions = array()) public function exists($entity, Criteria $extraConditions = null)
{ {
$criteria = $this->class->getIdentifierValues($entity); $criteria = $this->class->getIdentifierValues($entity);
@ -1843,22 +1843,25 @@ class BasicEntityPersister implements EntityPersister
return false; return false;
} }
if ($extraConditions) {
$criteria = array_merge($criteria, $extraConditions);
}
$alias = $this->getSQLTableAlias($this->class->name); $alias = $this->getSQLTableAlias($this->class->name);
$sql = 'SELECT 1 ' $sql = 'SELECT 1 '
. $this->getLockTablesSql(null) . $this->getLockTablesSql(null)
. ' WHERE ' . $this->getSelectConditionSQL($criteria); . ' WHERE ' . $this->getSelectConditionSQL($criteria);
list($params) = $this->expandParameters($criteria);
if (null !== $extraConditions) {
$sql .= ' AND ' . $this->getSelectConditionCriteriaSQL($extraConditions);
list($criteriaParams, $values) = $this->expandCriteriaParameters($extraConditions);
$params = array_merge($params, $criteriaParams);
}
if ($filterSql = $this->generateFilterConditionSQL($this->class, $alias)) { if ($filterSql = $this->generateFilterConditionSQL($this->class, $alias)) {
$sql .= ' AND ' . $filterSql; $sql .= ' AND ' . $filterSql;
} }
list($params) = $this->expandParameters($criteria);
return (bool) $this->conn->fetchColumn($sql, $params); return (bool) $this->conn->fetchColumn($sql, $params);
} }

View file

@ -319,10 +319,10 @@ interface EntityPersister
/** /**
* Checks whether the given managed entity exists in the database. * Checks whether the given managed entity exists in the database.
* *
* @param object $entity * @param object $entity
* @param array $extraConditions * @param Criteria|null $extraConditions
* *
* @return boolean TRUE if the entity exists in the database, FALSE otherwise. * @return boolean TRUE if the entity exists in the database, FALSE otherwise.
*/ */
public function exists($entity, array $extraConditions = array()); public function exists($entity, Criteria $extraConditions = null);
} }

View file

@ -19,6 +19,7 @@
namespace Doctrine\ORM\Persisters; namespace Doctrine\ORM\Persisters;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\UnitOfWork; use Doctrine\ORM\UnitOfWork;
@ -168,7 +169,7 @@ class OneToManyPersister extends AbstractCollectionPersister
return (bool) $this->conn->fetchColumn($sql, $params); return (bool) $this->conn->fetchColumn($sql, $params);
} }
private function getJoinTableRestrictions(PersistentCollection $coll, $addFilters) private function getJoinTableRestrictions(PersistentCollection $coll, $addFilters)
{ {
$mapping = $coll->getMapping(); $mapping = $coll->getMapping();
@ -227,9 +228,9 @@ class OneToManyPersister extends AbstractCollectionPersister
// only works with single id identifier entities. Will throw an // only works with single id identifier entities. Will throw an
// exception in Entity Persisters if that is not the case for the // exception in Entity Persisters if that is not the case for the
// 'mappedBy' field. // 'mappedBy' field.
$id = current($uow->getEntityIdentifier($coll->getOwner())); $criteria = new Criteria(Criteria::expr()->eq($mapping['mappedBy'], $coll->getOwner()));
return $persister->exists($element, array($mapping['mappedBy'] => $id)); return $persister->exists($element, $criteria);
} }
/** /**

View file

@ -1,6 +1,7 @@
<?php <?php
namespace Doctrine\Tests\Mocks; namespace Doctrine\Tests\Mocks;
use Doctrine\Common\Collections\Criteria;
/** /**
* EntityPersister implementation used for mocking during tests. * EntityPersister implementation used for mocking during tests.
@ -88,7 +89,7 @@ class EntityPersisterMock extends \Doctrine\ORM\Persisters\BasicEntityPersister
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function exists($entity, array $extraConditions = array()) public function exists($entity, Criteria $extraConditions = null)
{ {
$this->existsCalled = true; $this->existsCalled = true;
} }

View file

@ -433,8 +433,8 @@ abstract class AbstractEntityPersisterTest extends OrmTestCase
$this->entityPersister->expects($this->once()) $this->entityPersister->expects($this->once())
->method('exists') ->method('exists')
->with($this->equalTo($entity), $this->equalTo(array())); ->with($this->equalTo($entity), $this->equalTo(null));
$this->assertNull($persister->exists($entity, array())); $this->assertNull($persister->exists($entity));
} }
} }

View file

@ -21,6 +21,8 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\Generic\DateTimeModel; use Doctrine\Tests\Models\Generic\DateTimeModel;
use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Criteria;
use Doctrine\Tests\Models\Tweet\Tweet;
use Doctrine\Tests\Models\Tweet\User;
/** /**
* @author Josiah <josiah@jjs.id.au> * @author Josiah <josiah@jjs.id.au>
@ -30,6 +32,7 @@ class EntityRepositoryCriteriaTest extends \Doctrine\Tests\OrmFunctionalTestCase
protected function setUp() protected function setUp()
{ {
$this->useModelSet('generic'); $this->useModelSet('generic');
$this->useModelSet('tweet');
parent::setUp(); parent::setUp();
} }
@ -165,4 +168,34 @@ class EntityRepositoryCriteriaTest extends \Doctrine\Tests\OrmFunctionalTestCase
$date = $dates[0]; $date = $dates[0];
$this->assertTrue($dates->isInitialized()); $this->assertTrue($dates->isInitialized());
} }
public function testCanContainsWithoutLoadingCollection()
{
$user = new User();
$user->name = 'Marco';
$this->_em->persist($user);
$this->_em->flush();
$tweet = new Tweet();
$tweet->author = $user;
$tweet->content = 'Criteria is awesome';
$this->_em->persist($tweet);
$this->_em->flush();
$this->_em->clear();
$criteria = new Criteria();
$criteria->andWhere($criteria->expr()->contains('content', 'Criteria'));
$user = $this->_em->find('Doctrine\Tests\Models\Tweet\User', $user->id);
$tweets = $user->tweets->matching($criteria);
$this->assertInstanceOf('Doctrine\ORM\LazyCriteriaCollection', $tweets);
$this->assertFalse($tweets->isInitialized());
$tweets->contains($tweet);
$this->assertTrue($tweets->contains($tweet));
$this->assertFalse($tweets->isInitialized());
}
} }