Merge branch 'DDC-546'
This commit is contained in:
commit
7a2c99353a
@ -121,6 +121,7 @@
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="EAGER"/>
|
||||
<xs:enumeration value="LAZY"/>
|
||||
<xs:enumeration value="EXTRALAZY"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
|
@ -121,6 +121,12 @@ class ClassMetadataInfo
|
||||
* association is fetched.
|
||||
*/
|
||||
const FETCH_EAGER = 3;
|
||||
/**
|
||||
* Specifies that an association is to be fetched lazy (on first access) and that
|
||||
* commands such as Collection#count, Collection#slice are issued directly against
|
||||
* the database if the collection is not yet initialized.
|
||||
*/
|
||||
const FETCH_EXTRA_LAZY = 4;
|
||||
/**
|
||||
* Identifies a one-to-one association.
|
||||
*/
|
||||
|
@ -59,7 +59,7 @@ final class PersistentCollection implements Collection
|
||||
* The association mapping the collection belongs to.
|
||||
* This is currently either a OneToManyMapping or a ManyToManyMapping.
|
||||
*
|
||||
* @var Doctrine\ORM\Mapping\AssociationMapping
|
||||
* @var array
|
||||
*/
|
||||
private $association;
|
||||
|
||||
@ -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_EXTRA_LAZY) {
|
||||
return $this->coll->contains($element) ||
|
||||
$this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->contains($this, $element);
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
return $this->coll->contains($element);
|
||||
@ -475,6 +465,12 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
return $this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->count($this) + $this->coll->count();
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
return $this->coll->count();
|
||||
}
|
||||
@ -675,6 +671,12 @@ final class PersistentCollection implements Collection
|
||||
*/
|
||||
public function slice($offset, $length = null)
|
||||
{
|
||||
if (!$this->initialized && $this->association['fetch'] == Mapping\ClassMetadataInfo::FETCH_EXTRA_LAZY) {
|
||||
return $this->em->getUnitOfWork()
|
||||
->getCollectionPersister($this->association)
|
||||
->slice($this, $offset, $length);
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
return $this->coll->slice($offset, $length);
|
||||
}
|
||||
|
@ -125,6 +125,31 @@ abstract class AbstractCollectionPersister
|
||||
}
|
||||
}
|
||||
|
||||
public function count(PersistentCollection $coll)
|
||||
{
|
||||
throw new \BadMethodCallException("Counting the size of this persistent collection is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
public function slice(PersistentCollection $coll, $offset, $length = null)
|
||||
{
|
||||
throw new \BadMethodCallException("Slicing elements is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
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, $key)
|
||||
{
|
||||
throw new \BadMethodCallException("Checking for existance of a key is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
public function get(PersistentCollection $coll, $index)
|
||||
{
|
||||
throw new \BadMethodCallException("Selecting a collection by index is not supported by this CollectionPersister.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL statement used for deleting a row from the collection.
|
||||
*
|
||||
|
@ -717,15 +717,49 @@ class BasicEntityPersister
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get (sliced or full) elements of the given collection.
|
||||
*
|
||||
* @param array $assoc
|
||||
* @param object $sourceEntity
|
||||
* @param int|null $offset
|
||||
* @param int|null $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getManyToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
|
||||
{
|
||||
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity, $offset, $limit);
|
||||
|
||||
$entities = array();
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$entities[] = $this->_createEntity($result);
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a collection of entities of a many-to-many association.
|
||||
*
|
||||
* @param ManyToManyMapping $assoc The association mapping of the association being loaded.
|
||||
* @param object $sourceEntity The entity that owns the collection.
|
||||
* @param PersistentCollection $coll The collection to fill.
|
||||
* @param int|null $offset
|
||||
* @param int|null $limit
|
||||
* @return array
|
||||
*/
|
||||
public function loadManyToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
|
||||
{
|
||||
{
|
||||
$stmt = $this->getManyToManyStatement($assoc, $sourceEntity);
|
||||
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$coll->hydrateAdd($this->_createEntity($result));
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
|
||||
private function getManyToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
|
||||
{
|
||||
$criteria = array();
|
||||
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
|
||||
$joinTableConditions = array();
|
||||
@ -769,13 +803,9 @@ class BasicEntityPersister
|
||||
}
|
||||
}
|
||||
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc);
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
$stmt = $this->_conn->executeQuery($sql, $params, $types);
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$coll->hydrateAdd($this->_createEntity($result));
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
return $this->_conn->executeQuery($sql, $params, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -854,7 +884,7 @@ class BasicEntityPersister
|
||||
* @return string
|
||||
* @todo Refactor: _getSelectSQL(...)
|
||||
*/
|
||||
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0)
|
||||
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null)
|
||||
{
|
||||
$joinSql = $assoc != null && $assoc['type'] == ClassMetadata::MANY_TO_MANY ?
|
||||
$this->_getSelectManyToManyJoinSQL($assoc) : '';
|
||||
@ -872,12 +902,12 @@ class BasicEntityPersister
|
||||
$lockSql = ' ' . $this->_platform->getWriteLockSql();
|
||||
}
|
||||
|
||||
return 'SELECT ' . $this->_getSelectColumnListSQL()
|
||||
return $this->_platform->modifyLimitQuery('SELECT ' . $this->_getSelectColumnListSQL()
|
||||
. $this->_platform->appendLockHint(' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->_getSQLTableAlias($this->_class->name), $lockMode)
|
||||
. $joinSql
|
||||
. ($conditionSql ? ' WHERE ' . $conditionSql : '')
|
||||
. $orderBySql
|
||||
. $orderBySql, $limit, $offset)
|
||||
. $lockSql;
|
||||
}
|
||||
|
||||
@ -1175,14 +1205,56 @@ class BasicEntityPersister
|
||||
return $conditionSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with (sliced or full list) of elements in the specified collection.
|
||||
*
|
||||
* @param array $assoc
|
||||
* @param object $sourceEntity
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getOneToManyCollection(array $assoc, $sourceEntity, $offset = null, $limit = null)
|
||||
{
|
||||
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity, $offset, $limit);
|
||||
|
||||
$entities = array();
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$entities[] = $this->_createEntity($result);
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a collection of entities in a one-to-many association.
|
||||
*
|
||||
* @param OneToManyMapping $assoc
|
||||
* @param array $criteria The criteria by which to select the entities.
|
||||
* @param PersistentCollection The collection to load/fill.
|
||||
* @param array $assoc
|
||||
* @param object $sourceEntity
|
||||
* @param PersistentCollection $coll The collection to load/fill.
|
||||
* @param int|null $offset
|
||||
* @param int|null $limit
|
||||
*/
|
||||
public function loadOneToManyCollection(array $assoc, $sourceEntity, PersistentCollection $coll)
|
||||
{
|
||||
$stmt = $this->getOneToManyStatement($assoc, $sourceEntity);
|
||||
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$coll->hydrateAdd($this->_createEntity($result));
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build criteria and execute SQL statement to fetch the one to many entities from.
|
||||
*
|
||||
* @param array $assoc
|
||||
* @param object $sourceEntity
|
||||
* @param int|null $offset
|
||||
* @param int|null $limit
|
||||
* @return Doctrine\DBAL\Statement
|
||||
*/
|
||||
private function getOneToManyStatement(array $assoc, $sourceEntity, $offset = null, $limit = null)
|
||||
{
|
||||
$criteria = array();
|
||||
$owningAssoc = $this->_class->associationMappings[$assoc['mappedBy']];
|
||||
@ -1201,13 +1273,9 @@ class BasicEntityPersister
|
||||
}
|
||||
}
|
||||
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc);
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
$stmt = $this->_conn->executeQuery($sql, $params, $types);
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$coll->hydrateAdd($this->_createEntity($result));
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
return $this->_conn->executeQuery($sql, $params, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1237,19 +1305,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()
|
||||
{
|
||||
|
||||
}*/
|
||||
}
|
||||
|
@ -20,7 +20,8 @@
|
||||
namespace Doctrine\ORM\Persisters;
|
||||
|
||||
use Doctrine\ORM\ORMException,
|
||||
Doctrine\ORM\Mapping\ClassMetadata;
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\DBAL\LockMode;
|
||||
|
||||
/**
|
||||
* The joined subclass persister maps a single entity instance to several tables in the
|
||||
@ -239,7 +240,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0)
|
||||
protected function _getSelectEntitiesSQL(array $criteria, $assoc = null, $lockMode = 0, $limit = null, $offset = null)
|
||||
{
|
||||
$idColumns = $this->_class->getIdentifierColumnNames();
|
||||
$baseTableAlias = $this->_getSQLTableAlias($this->_class->name);
|
||||
@ -348,10 +349,18 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
$this->_selectColumnListSql = $columnList;
|
||||
}
|
||||
|
||||
return 'SELECT ' . $this->_selectColumnListSql
|
||||
$lockSql = '';
|
||||
if ($lockMode == LockMode::PESSIMISTIC_READ) {
|
||||
$lockSql = ' ' . $this->_platform->getReadLockSql();
|
||||
} else if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
|
||||
$lockSql = ' ' . $this->_platform->getWriteLockSql();
|
||||
}
|
||||
|
||||
return $this->_platform->modifyLimitQuery('SELECT ' . $this->_selectColumnListSql
|
||||
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' ' . $baseTableAlias
|
||||
. $joinSql
|
||||
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql;
|
||||
. ($conditionSql != '' ? ' WHERE ' . $conditionSql : '') . $orderBySql, $limit, $offset)
|
||||
. $lockSql;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
@ -181,4 +182,119 @@ class ManyToManyPersister extends AbstractCollectionPersister
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count(PersistentCollection $coll)
|
||||
{
|
||||
$params = array();
|
||||
$mapping = $coll->getMapping();
|
||||
$class = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
||||
|
||||
if ($mapping['isOwningSide']) {
|
||||
$joinTable = $mapping['joinTable'];
|
||||
$joinColumns = $mapping['relationToSourceKeyColumns'];
|
||||
} else {
|
||||
$mapping = $this->_em->getClassMetadata($mapping['targetEntity'])->associationMappings[$mapping['mappedBy']];
|
||||
$joinTable = $mapping['joinTable'];
|
||||
$joinColumns = $mapping['relationToTargetKeyColumns'];
|
||||
}
|
||||
|
||||
$whereClause = '';
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
if (isset($joinColumns[$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
$whereClause .= "$joinTableColumn = ?";
|
||||
|
||||
if ($class->containsForeignIdentifier) {
|
||||
$params[] = $id[$class->getFieldForColumn($joinColumns[$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $id[$class->fieldNames[$joinColumns[$joinTableColumn]]];
|
||||
}
|
||||
}
|
||||
}
|
||||
$sql = 'SELECT count(*) FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause;
|
||||
|
||||
return $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PersistentCollection $coll
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @return array
|
||||
*/
|
||||
public function slice(PersistentCollection $coll, $offset, $length = null)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
return $this->_em->getUnitOfWork()
|
||||
->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();
|
||||
|
||||
if (!$mapping['isOwningSide']) {
|
||||
$sourceClass = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
$targetClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||
$sourceId = $uow->getEntityIdentifier($element);
|
||||
$targetId = $uow->getEntityIdentifier($coll->getOwner());
|
||||
|
||||
$mapping = $sourceClass->associationMappings[$mapping['mappedBy']];
|
||||
} else {
|
||||
$sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
|
||||
$targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
$sourceId = $uow->getEntityIdentifier($coll->getOwner());
|
||||
$targetId = $uow->getEntityIdentifier($element);
|
||||
}
|
||||
$joinTable = $mapping['joinTable'];
|
||||
|
||||
$whereClause = '';
|
||||
foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
|
||||
if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
$whereClause .= "$joinTableColumn = ?";
|
||||
|
||||
if ($targetClass->containsForeignIdentifier) {
|
||||
$params[] = $targetId[$targetClass->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $targetId[$targetClass->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
} else if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
|
||||
if ($whereClause !== '') {
|
||||
$whereClause .= ' AND ';
|
||||
}
|
||||
$whereClause .= "$joinTableColumn = ?";
|
||||
|
||||
if ($sourceClass->containsForeignIdentifier) {
|
||||
$params[] = $sourceId[$sourceClass->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
|
||||
} else {
|
||||
$params[] = $sourceId[$sourceClass->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
|
||||
}
|
||||
}
|
||||
}
|
||||
$sql = 'SELECT 1 FROM ' . $joinTable['name'] . ' WHERE ' . $whereClause;
|
||||
|
||||
return (bool)$this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
}
|
@ -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.
|
||||
@ -116,4 +117,67 @@ class OneToManyPersister extends AbstractCollectionPersister
|
||||
*/
|
||||
protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
|
||||
{}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count(PersistentCollection $coll)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
$class = $this->_em->getClassMetadata($mapping['targetEntity']);
|
||||
$params = array();
|
||||
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
|
||||
|
||||
$where = '';
|
||||
foreach ($class->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
|
||||
if ($where != '') {
|
||||
$where .= ' AND ';
|
||||
}
|
||||
$where .= $joinColumn['name'] . " = ?";
|
||||
if ($class->containsForeignIdentifier) {
|
||||
$params[] = $id[$class->getFieldForColumn($joinColumn['referencedColumnName'])];
|
||||
} else {
|
||||
$params[] = $id[$class->fieldNames[$joinColumn['referencedColumnName']]];
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "SELECT count(*) FROM " . $class->getQuotedTableName($this->_conn->getDatabasePlatform()) . " WHERE " . $where;
|
||||
return $this->_conn->fetchColumn($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PersistentCollection $coll
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @return \Doctrine\Common\Collections\ArrayCollection
|
||||
*/
|
||||
public function slice(PersistentCollection $coll, $offset, $length = null)
|
||||
{
|
||||
$mapping = $coll->getMapping();
|
||||
return $this->_em->getUnitOfWork()
|
||||
->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));
|
||||
}
|
||||
}
|
@ -1958,11 +1958,11 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$reflField = $class->reflFields[$field];
|
||||
$reflField->setValue($entity, $pColl);
|
||||
|
||||
if ($assoc['fetch'] == ClassMetadata::FETCH_LAZY) {
|
||||
$pColl->setInitialized(false);
|
||||
} else {
|
||||
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
|
||||
$this->loadCollection($pColl);
|
||||
$pColl->takeSnapshot();
|
||||
} else {
|
||||
$pColl->setInitialized(false);
|
||||
}
|
||||
$this->originalEntityData[$oid][$field] = $pColl;
|
||||
}
|
||||
@ -2123,7 +2123,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
* Gets the EntityPersister for an Entity.
|
||||
*
|
||||
* @param string $entityName The name of the Entity.
|
||||
* @return Doctrine\ORM\Persister\AbstractEntityPersister
|
||||
* @return Doctrine\ORM\Persisters\AbstractEntityPersister
|
||||
*/
|
||||
public function getEntityPersister($entityName)
|
||||
{
|
||||
|
@ -59,7 +59,7 @@ class EntityPersisterMock extends \Doctrine\ORM\Persisters\BasicEntityPersister
|
||||
$this->_updates[] = $entity;
|
||||
}
|
||||
|
||||
public function exists($entity)
|
||||
public function exists($entity, array $extraConditions = array())
|
||||
{
|
||||
$this->existsCalled = true;
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ class AllTests
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\IdentityMapTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\DatabaseDriverTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\PostgreSQLIdentityStrategyTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ExtraLazyCollectionTest');
|
||||
|
||||
$suite->addTest(Locking\AllTests::suite());
|
||||
$suite->addTest(Ticket\AllTests::suite());
|
||||
|
@ -9,8 +9,6 @@ use Doctrine\Tests\Models\CMS\CmsAddress;
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
/**
|
||||
* Description of DetachedEntityTest
|
||||
*
|
||||
* @author robo
|
||||
*/
|
||||
class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
375
tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php
Normal file
375
tests/Doctrine/Tests/ORM/Functional/ExtraLazyCollectionTest.php
Normal file
@ -0,0 +1,375 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
/**
|
||||
* Description of ExtraLazyCollectionTest
|
||||
*
|
||||
* @author beberlei
|
||||
*/
|
||||
class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
private $userId;
|
||||
private $groupId;
|
||||
private $articleId;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
|
||||
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
|
||||
$class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
|
||||
|
||||
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup');
|
||||
$class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_EXTRA_LAZY;
|
||||
|
||||
|
||||
$this->loadFixture();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser');
|
||||
$class->associationMappings['groups']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
|
||||
$class->associationMappings['articles']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
|
||||
|
||||
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup');
|
||||
$class->associationMappings['users']['fetch'] = ClassMetadataInfo::FETCH_LAZY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testCountNotInitializesCollection()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$this->assertFalse($user->groups->isInitialized());
|
||||
$this->assertEquals(3, count($user->groups));
|
||||
$this->assertFalse($user->groups->isInitialized());
|
||||
|
||||
foreach ($user->groups AS $group) { }
|
||||
|
||||
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount(), "Expecting two queries to be fired for count, then iteration.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testCountWhenNewEntitysPresent()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
|
||||
$newGroup = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
||||
$newGroup->name = "Test4";
|
||||
|
||||
$user->addGroup($newGroup);
|
||||
$this->_em->persist($newGroup);
|
||||
|
||||
$this->assertFalse($user->groups->isInitialized());
|
||||
$this->assertEquals(4, count($user->groups));
|
||||
$this->assertFalse($user->groups->isInitialized());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testCountWhenInitialized()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
foreach ($user->groups AS $group) { }
|
||||
|
||||
$this->assertTrue($user->groups->isInitialized());
|
||||
$this->assertEquals(3, count($user->groups));
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount(), "Should only execute one query to initialize colleciton, no extra query for count() more.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testCountInverseCollection()
|
||||
{
|
||||
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
||||
$this->assertFalse($group->users->isInitialized(), "Pre-Condition");
|
||||
|
||||
$this->assertEquals(4, count($group->users));
|
||||
$this->assertFalse($group->users->isInitialized(), "Extra Lazy collection should not be initialized by counting the collection.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testCountOneToMany()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition");
|
||||
|
||||
$this->assertEquals(2, count($user->articles));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testFullSlice()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||
|
||||
$someGroups = $user->groups->slice(null);
|
||||
$this->assertEquals(3, count($someGroups));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testSlice()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$this->assertFalse($user->groups->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$someGroups = $user->groups->slice(0, 2);
|
||||
|
||||
$this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsGroup', $someGroups);
|
||||
$this->assertEquals(2, count($someGroups));
|
||||
$this->assertFalse($user->groups->isInitialized(), "Slice should not initialize the collection if it wasn't before!");
|
||||
|
||||
$otherGroup = $user->groups->slice(2, 1);
|
||||
|
||||
$this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsGroup', $otherGroup);
|
||||
$this->assertEquals(1, count($otherGroup));
|
||||
$this->assertFalse($user->groups->isInitialized());
|
||||
|
||||
foreach ($user->groups AS $group) { }
|
||||
|
||||
$this->assertTrue($user->groups->isInitialized());
|
||||
$this->assertEquals(3, count($user->groups));
|
||||
|
||||
$this->assertEquals($queryCount + 3, $this->getCurrentQueryCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testSliceInitializedCollection()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
foreach ($user->groups AS $group) { }
|
||||
|
||||
$someGroups = $user->groups->slice(0, 2);
|
||||
|
||||
$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
|
||||
|
||||
$this->assertEquals(2, count($someGroups));
|
||||
$this->assertTrue($user->groups->contains($someGroups[0]));
|
||||
$this->assertTrue($user->groups->contains($someGroups[1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testSliceInverseCollection()
|
||||
{
|
||||
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
||||
$this->assertFalse($group->users->isInitialized(), "Pre-Condition");
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$someUsers = $group->users->slice(0, 2);
|
||||
$otherUsers = $group->users->slice(2, 2);
|
||||
|
||||
$this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsUser', $someUsers);
|
||||
$this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsUser', $otherUsers);
|
||||
$this->assertEquals(2, count($someUsers));
|
||||
$this->assertEquals(2, count($otherUsers));
|
||||
|
||||
// +2 queries executed by slice, +4 are executed by EAGER fetching of User Address.
|
||||
$this->assertEquals($queryCount + 2 + 4, $this->getCurrentQueryCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testSliceOneToMany()
|
||||
{
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
$this->assertFalse($user->articles->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
|
||||
$someArticle = $user->articles->slice(0, 1);
|
||||
$otherArticle = $user->articles->slice(1, 1);
|
||||
|
||||
$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.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-546
|
||||
*/
|
||||
public function testContainsManyToManyInverse()
|
||||
{
|
||||
$group = $this->_em->find('Doctrine\Tests\Models\CMS\CmsGroup', $this->groupId);
|
||||
$this->assertFalse($group->users->isInitialized(), "Pre-Condition: Collection is not initialized.");
|
||||
|
||||
$user = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $this->userId);
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
$this->assertTrue($group->users->contains($user));
|
||||
$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.");
|
||||
|
||||
$newUser = new \Doctrine\Tests\Models\CMS\CmsUser();
|
||||
$newUser->name = "A New group!";
|
||||
|
||||
$queryCount = $this->getCurrentQueryCount();
|
||||
$this->assertFalse($group->users->contains($newUser));
|
||||
$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.");
|
||||
}
|
||||
|
||||
private function loadFixture()
|
||||
{
|
||||
$user1 = new \Doctrine\Tests\Models\CMS\CmsUser();
|
||||
$user1->username = "beberlei";
|
||||
$user1->name = "Benjamin";
|
||||
$user1->status = "active";
|
||||
|
||||
$user2 = new \Doctrine\Tests\Models\CMS\CmsUser();
|
||||
$user2->username = "jwage";
|
||||
$user2->name = "Jonathan";
|
||||
$user2->status = "active";
|
||||
|
||||
$user3 = new \Doctrine\Tests\Models\CMS\CmsUser();
|
||||
$user3->username = "romanb";
|
||||
$user3->name = "Roman";
|
||||
$user3->status = "active";
|
||||
|
||||
$user4 = new \Doctrine\Tests\Models\CMS\CmsUser();
|
||||
$user4->username = "gblanco";
|
||||
$user4->name = "Guilherme";
|
||||
$user4->status = "active";
|
||||
|
||||
$this->_em->persist($user1);
|
||||
$this->_em->persist($user2);
|
||||
$this->_em->persist($user3);
|
||||
$this->_em->persist($user4);
|
||||
|
||||
$group1 = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
||||
$group1->name = "Test1";
|
||||
|
||||
$group2 = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
||||
$group2->name = "Test2";
|
||||
|
||||
$group3 = new \Doctrine\Tests\Models\CMS\CmsGroup();
|
||||
$group3->name = "Test3";
|
||||
|
||||
$user1->addGroup($group1);
|
||||
$user1->addGroup($group2);
|
||||
$user1->addGroup($group3);
|
||||
|
||||
$user2->addGroup($group1);
|
||||
$user3->addGroup($group1);
|
||||
$user4->addGroup($group1);
|
||||
|
||||
$this->_em->persist($group1);
|
||||
$this->_em->persist($group2);
|
||||
$this->_em->persist($group3);
|
||||
|
||||
$article1 = new \Doctrine\Tests\Models\CMS\CmsArticle();
|
||||
$article1->topic = "Test";
|
||||
$article1->text = "Test";
|
||||
$article1->setAuthor($user1);
|
||||
|
||||
$article2 = new \Doctrine\Tests\Models\CMS\CmsArticle();
|
||||
$article2->topic = "Test";
|
||||
$article2->text = "Test";
|
||||
$article2->setAuthor($user1);
|
||||
|
||||
$this->_em->persist($article1);
|
||||
$this->_em->persist($article2);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$this->articleId = $article1->id;
|
||||
$this->userId = $user1->getId();
|
||||
$this->groupId = $group1->id;
|
||||
}
|
||||
}
|
@ -314,4 +314,14 @@ abstract class OrmFunctionalTestCase extends OrmTestCase
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the SQL Logger Stack this method retrieves the current query count executed in this test.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getCurrentQueryCount()
|
||||
{
|
||||
return count($this->_sqlLoggerStack->queries);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user