Commit 2ec4cc5c authored by romanb's avatar romanb

[2.0] More cleanups for recent lazy-loading implementation and minor object...

[2.0] More cleanups for recent lazy-loading implementation and minor object hydration improvements and cleanups. Collection refactoring part I for ticket #2352.
parent 62446f0f
......@@ -21,11 +21,7 @@
namespace Doctrine\Common\Collections;
use \Closure;
use \Countable;
use \IteratorAggregate;
use \ArrayAccess;
use \ArrayIterator;
use \Closure, \ArrayIterator;
/**
* A Collection is a thin wrapper around a php array. Like a php array it is essentially
......@@ -34,7 +30,7 @@ use \ArrayIterator;
* @author Roman S. Borschel <roman@code-factory.org>
* @since 2.0
*/
class Collection implements Countable, IteratorAggregate, ArrayAccess
class ArrayCollection implements ICollection
{
/**
* An array containing the entries of this collection.
......@@ -42,10 +38,10 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess
*
* @var array
*/
protected $_elements;
private $_elements;
/**
* Initializes a new Collection.
* Initializes a new ArrayCollection.
*
* @param array $elements
*/
......@@ -64,6 +60,11 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess
return $this->_elements;
}
public function toArray()
{
return $this->_elements;
}
/**
* Gets the first element in the collection.
*
......@@ -249,7 +250,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess
*
* @return array
*/
public function getElements()
public function getValues()
{
return array_values($this->_elements);
}
......@@ -323,7 +324,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess
*/
public function map(Closure $func)
{
return new Collection(array_map($func, $this->_elements));
return new ArrayCollection(array_map($func, $this->_elements));
}
/**
......@@ -335,7 +336,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess
*/
public function filter(Closure $p)
{
return new Collection(array_filter($this->_elements, $p));
return new ArrayCollection(array_filter($this->_elements, $p));
}
/**
......@@ -374,7 +375,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess
$coll2[$key] = $element;
}
}
return array(new Collection($coll1), new Collection($coll2));
return array(new ArrayCollection($coll1), new ArrayCollection($coll2));
}
/**
......
......@@ -21,9 +21,9 @@
namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Types\Type;
use Doctrine\Common\DoctrineException;
use Doctrine\DBAL\Connection,
Doctrine\DBAL\Types\Type,
Doctrine\Common\DoctrineException;
/**
* Base class for all hydrators. A hydrator is a class that provides some form
......@@ -151,7 +151,7 @@ abstract class AbstractHydrator
*/
protected function _hydrateRow(array &$data, array &$cache, &$result)
{
throw new DoctrineException("_hydrateRow() not implemented for this hydrator.");
throw new DoctrineException("_hydrateRow() not implemented by this hydrator.");
}
/**
......
......@@ -21,10 +21,11 @@
namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Query;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Connection,
Doctrine\ORM\PersistentCollection,
Doctrine\ORM\Query,
Doctrine\Common\Collections\ArrayCollection,
Doctrine\Common\Collections\ICollection;
/**
* The ObjectHydrator constructs an object graph out of an SQL result set.
......@@ -51,11 +52,8 @@ class ObjectHydrator extends AbstractHydrator
private $_resultCounter;
private $_rootAliases = array();
private $_fetchedAssociations;
/* TODO: Consider unifying _collections and _initializedRelations */
/** Collections initialized by the hydrator */
private $_collections = array();
/** Memory for initialized relations */
private $_initializedRelations = array();
/** Memory for initialized collections. */
private $_initializedCollections = array();
/** @override */
protected function _prepare()
......@@ -76,11 +74,11 @@ class ObjectHydrator extends AbstractHydrator
$this->_idTemplate[$dqlAlias] = '';
$class = $this->_em->getClassMetadata($className);
if ( ! isset($this->_ce[$class->name])) {
$this->_ce[$class->name] = $class;
if ( ! isset($this->_ce[$className])) {
$this->_ce[$className] = $class;
// Gather class descriptors and discriminator values of subclasses, if necessary
if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) {
$this->_discriminatorMap[$class->name][$class->discriminatorValue] = $class->name;
$this->_discriminatorMap[$className][$class->discriminatorValue] = $className;
foreach (array_merge($class->parentClasses, $class->subClasses) as $className) {
$otherClass = $this->_em->getClassMetadata($className);
$value = $otherClass->discriminatorValue;
......@@ -115,7 +113,7 @@ class ObjectHydrator extends AbstractHydrator
*/
protected function _hydrateAll()
{
$result = $this->_rsm->isMixed ? array() : new Collection;
$result = $this->_rsm->isMixed ? array() : new ArrayCollection;
$cache = array();
while ($data = $this->_stmt->fetch(Connection::FETCH_ASSOC)) {
......@@ -123,13 +121,10 @@ class ObjectHydrator extends AbstractHydrator
}
// Take snapshots from all initialized collections
foreach ($this->_collections as $coll) {
foreach ($this->_initializedCollections as $coll) {
$coll->takeSnapshot();
}
// Clean up
$this->_collections = array();
$this->_initializedRelations = array();
$this->_initializedCollections = array();
return $result;
}
......@@ -141,16 +136,9 @@ class ObjectHydrator extends AbstractHydrator
* @param Collection $coll The element.
* @param boolean|integer $index Index of the element in the collection.
* @param string $dqlAlias
* @todo May be worth to try to inline this method (through first reducing the
* calls of this method to 1).
*/
private function updateResultPointer(&$coll, $index, $dqlAlias)
{
if ($coll === null) {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
return;
}
if ($index !== false) {
$this->_resultPointers[$dqlAlias] = $coll[$index];
return;
......@@ -159,7 +147,7 @@ class ObjectHydrator extends AbstractHydrator
if ( ! is_object($coll)) {
end($coll);
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
} else if ($coll instanceof Collection) {
} else if ($coll instanceof ICollection) {
if (count($coll) > 0) {
$this->_resultPointers[$dqlAlias] = $coll->last();
}
......@@ -178,37 +166,17 @@ class ObjectHydrator extends AbstractHydrator
{
$oid = spl_object_hash($entity);
$class = $this->_ce[get_class($entity)];
$relation = $class->associationMappings[$name];
//$coll = $class->reflFields[$name]->getValue($entity);
//if ( ! $coll) {
// $coll = new Collection;
//}
$pColl = new PersistentCollection($this->_em, $this->_getClassMetadata($relation->targetEntityName),
$class->reflFields[$name]->getValue($entity) ?: new ArrayCollection);
$pColl = new PersistentCollection($this->_em, $this->_getClassMetadata($relation->targetEntityName));
$this->_collections[] = $pColl;
$pColl->setOwner($entity, $relation);
$class->reflFields[$name]->setValue($entity, $pColl);
$this->_uow->setOriginalEntityProperty($oid, $name, $pColl);
$this->_initializedRelations[$oid][$name] = true;
}
$this->_initializedCollections[$oid . $name] = $pColl;
/**
*
* @param <type> $entity
* @param <type> $assocField
* @param <type> $indexField
* @return <type>
* @todo Inline this method.
*/
private function isIndexKeyInUse($entity, $assocField, $indexField)
{
return $this->_ce[get_class($entity)]
->reflFields[$assocField]
->getValue($entity)
->containsKey($indexField);
return $pColl;
}
/**
......@@ -245,6 +213,7 @@ class ObjectHydrator extends AbstractHydrator
$className = $this->_discriminatorMap[$className][$data[$discrColumn]];
unset($data[$discrColumn]);
}
$entity = $this->_uow->createEntity($className, $data, $this->_hints);
// Properly initialize any unfetched associations, if partial objects are not allowed.
......@@ -269,9 +238,12 @@ class ObjectHydrator extends AbstractHydrator
}
} else {
// Inject collection
$pColl = new PersistentCollection($this->_em, $this->_getClassMetadata($assoc->targetEntityName));
$reflField = $this->_ce[$className]->reflFields[$field];
$pColl = new PersistentCollection($this->_em, $this->_getClassMetadata(
$assoc->targetEntityName), $reflField->getValue($entity) ?: new ArrayCollection
);
$pColl->setOwner($entity, $assoc);
$this->_ce[$className]->reflFields[$field]->setValue($entity, $pColl);
$reflField->setValue($entity, $pColl);
if ( ! $assoc->isLazilyFetched()) {
//TODO: Allow more efficient and configurable batching of these loads
$assoc->load($entity, $pColl, $this->_em);
......@@ -302,38 +274,6 @@ class ObjectHydrator extends AbstractHydrator
return $this->_ce[$className];
}
/**
* Sets a related element.
*
* @param object $entity1
* @param string $property
* @param object $entity2
*/
private function setRelatedElement($entity1, $property, $entity2)
{
$class = $this->_ce[get_class($entity1)];
$class->reflFields[$property]->setValue($entity1, $entity2);
$this->_uow->setOriginalEntityProperty(spl_object_hash($entity1), $property, $entity2);
$relation = $class->associationMappings[$property];
if ($relation->isOneToOne()) {
$targetClass = $this->_ce[$relation->targetEntityName];
if ($relation->isOwningSide) {
// If there is an inverse mapping on the target class its bidirectional
if (isset($targetClass->inverseMappings[$property])) {
$sourceProp = $targetClass->inverseMappings[$property]->sourceFieldName;
$targetClass->reflFields[$sourceProp]->setValue($entity2, $entity1);
} else if ($class === $targetClass && $relation->mappedByFieldName) {
// Special case: bi-directional self-referencing one-one on the same class
$targetClass->reflFields[$property]->setValue($entity2, $entity1);
}
} else {
// For sure bidirectional, as there is no inverse side in unidirectional mappings
$targetClass->reflFields[$relation->mappedByFieldName]->setValue($entity2, $entity1);
}
}
}
/**
* {@inheritdoc}
*
......@@ -362,7 +302,7 @@ class ObjectHydrator extends AbstractHydrator
$parent = $this->_rsm->parentAliasMap[$dqlAlias];
$relation = $this->_rsm->relationMap[$dqlAlias];
$relationAlias = $relation->sourceFieldName;
$relationField = $relation->sourceFieldName;
// Get a reference to the right element in the result tree.
// This element will get the associated element attached.
......@@ -378,34 +318,38 @@ class ObjectHydrator extends AbstractHydrator
}
$parentClass = get_class($baseElement);
$oid = spl_object_hash($baseElement);
$reflField = $this->_ce[$parentClass]->reflFields[$relationField];
$reflFieldValue = $reflField->getValue($baseElement);
// Check the type of the relation (many or single-valued)
if ( ! $relation->isOneToOne()) {
// Collection-valued association
if (isset($nonemptyComponents[$dqlAlias])) {
if ( ! isset($this->_initializedRelations[spl_object_hash($baseElement)][$relationAlias])) {
$this->initRelatedCollection($baseElement, $relationAlias);
if ( ! isset($this->_initializedCollections[$oid . $relationField])) {
$reflFieldValue = $this->initRelatedCollection($baseElement, $relationField);
}
$path = $parent . '.' . $dqlAlias;
$indexExists = isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
$index = $indexExists ? $this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
$indexIsValid = $index !== false ? $this->isIndexKeyInUse($baseElement, $relationAlias, $index) : false;
$indexIsValid = $index !== false ? $reflFieldValue->containsKey($index) : false;
if ( ! $indexExists || ! $indexIsValid) {
$element = $this->getEntity($data, $dqlAlias);
// If it's a bi-directional many-to-many, also initialize the reverse collection.
if ($relation->isManyToMany()) {
if ($relation->isOwningSide && isset($this->_ce[$entityName]->inverseMappings[$relationAlias])) {
$inverseFieldName = $this->_ce[$entityName]->inverseMappings[$relationAlias]->sourceFieldName;
if ($relation->isOwningSide && isset($this->_ce[$entityName]->inverseMappings[$relationField])) {
$inverseFieldName = $this->_ce[$entityName]->inverseMappings[$relationField]->sourceFieldName;
// Only initialize reverse collection if it is not yet initialized.
if ( ! isset($this->_initializedRelations[spl_object_hash($element)][$inverseFieldName])) {
if ( ! isset($this->_initializedCollections[spl_object_hash($element) . $inverseFieldName])) {
$this->initRelatedCollection($element, $this->_ce[$entityName]
->inverseMappings[$relationAlias]->sourceFieldName);
->inverseMappings[$relationField]->sourceFieldName);
}
} else if ($relation->mappedByFieldName) {
// Only initialize reverse collection if it is not yet initialized.
if ( ! isset($this->_initializedRelations[spl_object_hash($element)][$relation->mappedByFieldName])) {
if ( ! isset($this->_initializedCollections[spl_object_hash($element) . $relation->mappedByFieldName])) {
$this->initRelatedCollection($element, $relation->mappedByFieldName);
}
}
......@@ -416,40 +360,49 @@ class ObjectHydrator extends AbstractHydrator
$indexValue = $this->_ce[$entityName]
->reflFields[$field]
->getValue($element);
$this->_ce[$parentClass]
->reflFields[$relationAlias]
->getValue($baseElement)
->hydrateSet($indexValue, $element);
$reflFieldValue->hydrateSet($indexValue, $element);
} else {
$this->_ce[$parentClass]
->reflFields[$relationAlias]
->getValue($baseElement)
->hydrateAdd($element);
$reflFieldValue->hydrateAdd($element);
}
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $this->getLastKey(
$this->_ce[$parentClass]
->reflFields[$relationAlias]
->getValue($baseElement)
);
$this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $this->getLastKey($reflFieldValue);
}
} else if ( ! $this->_ce[$parentClass]->reflFields[$relationAlias]->getValue($baseElement)) {
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName]);
$this->_collections[] = $coll;
$this->setRelatedElement($baseElement, $relationAlias, $coll);
} else if ( ! $reflFieldValue) {
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection);
$reflField->setValue($baseElement, $coll);
$reflFieldValue = $coll;
$this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
}
$this->updateResultPointer($reflFieldValue, $index, $dqlAlias);
} else {
if ( ! $this->_ce[$parentClass]->reflFields[$relationAlias]->getValue($baseElement)) {
// Single-valued association
$reflFieldValue = $reflField->getValue($baseElement);
if ( ! $reflFieldValue) {
if (isset($nonemptyComponents[$dqlAlias])) {
$this->setRelatedElement($baseElement, $relationAlias, $this->getEntity($data, $dqlAlias));
$element = $this->getEntity($data, $dqlAlias);
$reflField->setValue($baseElement, $element);
$this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
$targetClass = $this->_ce[$relation->targetEntityName];
if ($relation->isOwningSide) {
// If there is an inverse mapping on the target class its bidirectional
if (isset($targetClass->inverseMappings[$relationField])) {
$sourceProp = $targetClass->inverseMappings[$relationField]->sourceFieldName;
$targetClass->reflFields[$sourceProp]->setValue($element, $base);
} else if ($this->_ce[$parentClass] === $targetClass && $relation->mappedByFieldName) {
// Special case: bi-directional self-referencing one-one on the same class
$targetClass->reflFields[$relationField]->setValue($element, $baseElement);
}
} else {
// For sure bidirectional, as there is no inverse side in unidirectional mappings
$targetClass->reflFields[$relation->mappedByFieldName]->setValue($element, $baseElement);
}
}
}
$coll = $this->_ce[$parentClass]->reflFields[$relationAlias]->getValue($baseElement);
if ($coll !== null) {
$this->updateResultPointer($coll, $index, $dqlAlias);
if ($reflFieldValue !== null) {
$this->updateResultPointer($reflFieldValue, $index, $dqlAlias);
}
}
} else {
// Its a root result element
......@@ -499,6 +452,6 @@ class ObjectHydrator extends AbstractHydrator
/** {@inheritdoc} */
protected function _getRowContainer()
{
return new \Doctrine\Common\Collections\Collection;
return new \Doctrine\Common\Collections\ArrayCollection;
}
}
......@@ -112,6 +112,11 @@ class ClassMetadataFactory
return $this->_loadedMetadata[$className];
}
public function hasMetadataFor($className)
{
return isset($this->_loadedMetadata[$className]);
}
/**
* Sets the metadata descriptor for a specific class.
*
......
......@@ -21,9 +21,9 @@
namespace Doctrine\ORM;
use Doctrine\Common\DoctrineException;
use Doctrine\ORM\Mapping\AssociationMapping;
use \Closure;
use Doctrine\Common\DoctrineException,
Doctrine\ORM\Mapping\AssociationMapping,
\Closure;
/**
* A PersistentCollection represents a collection of elements that have persistent state.
......@@ -44,7 +44,7 @@ use \Closure;
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
*/
final class PersistentCollection extends \Doctrine\Common\Collections\Collection
final class PersistentCollection implements \Doctrine\Common\Collections\ICollection
{
/**
* A snapshot of the collection at the moment it was fetched from the database.
......@@ -97,15 +97,30 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
*/
private $_isDirty = false;
/** Whether the collection has already been initialized. */
/**
* Whether the collection has already been initialized.
*
* @var boolean
*/
private $_initialized = true;
/**
* The wrapped Collection instance.
*
* @var Collection
*/
private $_coll;
/**
* Creates a new persistent collection.
*
* @param EntityManager $em The EntityManager the collection will be associated with.
* @param ClassMetadata $class The class descriptor of the entity type of this collection.
* @param array The collection elements.
*/
public function __construct(EntityManager $em, $class, array $data = array())
public function __construct(EntityManager $em, $class, $coll)
{
parent::__construct($data);
$this->_coll = $coll;
$this->_em = $em;
$this->_typeClass = $class;
}
......@@ -122,14 +137,13 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
$this->_owner = $entity;
$this->_association = $assoc;
// Check for bidirectionality
if ($assoc->isInverseSide()) {
if ( ! $assoc->isOwningSide) {
// For sure bi-directional
$this->_backRefFieldName = $assoc->mappedByFieldName;
} else {
$targetClass = $this->_em->getClassMetadata($assoc->targetEntityName);
if (isset($targetClass->inverseMappings[$assoc->sourceFieldName])) {
if (isset($this->_typeClass->inverseMappings[$assoc->sourceFieldName])) {
// Bi-directional
$this->_backRefFieldName = $targetClass->inverseMappings[$assoc->sourceFieldName]->sourceFieldName;
$this->_backRefFieldName = $this->_typeClass->inverseMappings[$assoc->sourceFieldName]->sourceFieldName;
}
}
}
......@@ -163,7 +177,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
*/
public function hydrateAdd($element)
{
parent::add($element);
$this->_coll->add($element);
// If _backRefFieldName is set, then the association is bidirectional
// and we need to set the back reference.
if ($this->_backRefFieldName) {
......@@ -189,7 +203,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
*/
public function hydrateSet($key, $value)
{
parent::set($key, $value);
$this->_coll->set($key, $value);
}
/**
......@@ -210,7 +224,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
*/
public function takeSnapshot()
{
$this->_snapshot = $this->_elements;
$this->_snapshot = $this->_coll->unwrap();
}
/**
......@@ -232,7 +246,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
*/
public function getDeleteDiff()
{
return array_udiff($this->_snapshot, $this->_elements, array($this, '_compareRecords'));
return array_udiff($this->_snapshot, $this->_coll->unwrap(), array($this, '_compareRecords'));
}
/**
......@@ -242,7 +256,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
*/
public function getInsertDiff()
{
return array_udiff($this->_elements, $this->_snapshot, array($this, '_compareRecords'));
return array_udiff($this->_coll->unwrap(), $this->_snapshot, array($this, '_compareRecords'));
}
/**
......@@ -319,14 +333,14 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function first()
{
$this->_initialize();
return parent::first();
return $this->_coll->first();
}
/** {@inheritdoc} */
public function last()
{
$this->_initialize();
return parent::last();
return $this->_coll->last();
}
/**
......@@ -335,7 +349,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function remove($key)
{
$this->_initialize();
$removed = parent::remove($key);
$removed = $this->_coll->remove($key);
if ($removed) {
$this->_changed();
if ($this->_association->isOneToMany() && $this->_association->orphanRemoval) {
......@@ -352,7 +366,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function removeElement($element)
{
$this->_initialize();
$result = parent::removeElement($element);
$result = $this->_coll->removeElement($element);
$this->_changed();
return $result;
}
......@@ -363,7 +377,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function containsKey($key)
{
$this->_initialize();
return parent::containsKey($key);
return $this->_coll->containsKey($key);
}
/**
......@@ -372,7 +386,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function contains($element)
{
$this->_initialize();
return parent::contains($element);
return $this->_coll->contains($element);
}
/**
......@@ -381,7 +395,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function exists(Closure $p)
{
$this->_initialize();
return parent::exists($p);
return $this->_coll->exists($p);
}
/**
......@@ -390,7 +404,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function search($element)
{
$this->_initialize();
return parent::search($element);
return $this->_coll->search($element);
}
/**
......@@ -399,7 +413,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function get($key)
{
$this->_initialize();
return parent::get($key);
return $this->_coll->get($key);
}
/**
......@@ -408,16 +422,16 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function getKeys()
{
$this->_initialize();
return parent::getKeys();
return $this->_coll->getKeys();
}
/**
* {@inheritdoc}
*/
public function getElements()
public function getValues()
{
$this->_initialize();
return parent::getElements();
return $this->_coll->getValues();
}
/**
......@@ -426,7 +440,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function count()
{
$this->_initialize();
return parent::count();
return $this->_coll->count();
}
/**
......@@ -434,7 +448,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
*/
public function set($key, $value)
{
parent::set($key, $value);
$this->_coll->set($key, $value);
$this->_changed();
}
......@@ -443,7 +457,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
*/
public function add($value)
{
parent::add($value);
$this->_coll->add($value);
$this->_changed();
return true;
}
......@@ -454,7 +468,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function isEmpty()
{
$this->_initialize();
return parent::isEmpty();
return $this->_coll->isEmpty();
}
/**
......@@ -463,7 +477,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function getIterator()
{
$this->_initialize();
return parent::getIterator();
return $this->_coll->getIterator();
}
/**
......@@ -472,7 +486,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function map(Closure $func)
{
$this->_initialize();
$result = parent::map($func);
$result = $this->_coll->map($func);
$this->_changed();
return $result;
}
......@@ -483,7 +497,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function filter(Closure $p)
{
$this->_initialize();
return parent::filter($p);
return $this->_coll->filter($p);
}
/**
......@@ -492,7 +506,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function forAll(Closure $p)
{
$this->_initialize();
return parent::forAll($p);
return $this->_coll->forAll($p);
}
/**
......@@ -501,7 +515,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function partition(Closure $p)
{
$this->_initialize();
return parent::partition($p);
return $this->_coll->partition($p);
}
/**
......@@ -510,7 +524,7 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
public function clear()
{
$this->_initialize();
$result = parent::clear();
$result = $this->_coll->clear();
if ($this->_association->isOwningSide) {
$this->_changed();
$this->_em->getUnitOfWork()->scheduleCollectionDeletion($this);
......@@ -528,6 +542,59 @@ final class PersistentCollection extends \Doctrine\Common\Collections\Collection
*/
public function __sleep()
{
return array('_elements');
return array('_coll');
}
/* ArrayAccess implementation */
/**
* @see containsKey()
*/
public function offsetExists($offset)
{
return $this->containsKey($offset);
}
/**
* @see get()
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* @see add()
* @see set()
*/
public function offsetSet($offset, $value)
{
if ( ! isset($offset)) {
return $this->add($value);
}
return $this->set($offset, $value);
}
/**
* @see remove()
*/
public function offsetUnset($offset)
{
return $this->remove($offset);
}
public function toArray()
{
return $this->_coll->toArray();
}
public function key()
{
return $this->_coll->key();
}
public function unwrap()
{
return $this->_coll;
}
}
......@@ -22,6 +22,7 @@
namespace Doctrine\ORM\Persisters;
use Doctrine\Common\DoctrineException;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
......@@ -490,7 +491,8 @@ class StandardEntityPersister
}
} else {
// Inject collection
$coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName));
$coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName),
new ArrayCollection);
$coll->setOwner($entity, $assoc);
$this->_class->reflFields[$field]->setValue($entity, $coll);
if ($assoc->isLazilyFetched()) {
......
......@@ -20,6 +20,7 @@
*/
namespace Doctrine\ORM\Proxy;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
......@@ -85,9 +86,12 @@ class ProxyClassGenerator
protected function _generateClass($originalClassName, $proxyClassName, $file)
{
$class = $this->_em->getClassMetadata($originalClassName);
$proxyFullyQualifiedClassName = self::$_ns . $proxyClassName;
if ($this->_em->getMetadataFactory()->hasMetadataFor($proxyFullyQualifiedClassName)) {
return $proxyFullyQualifiedClassName;
}
$class = $this->_em->getClassMetadata($originalClassName);
$this->_em->getMetadataFactory()->setMetadataFor($proxyFullyQualifiedClassName, $class);
......@@ -214,7 +218,7 @@ namespace Doctrine\Generated\Proxies {
public function __sleep() {
if (!$this->_loaded) {
throw new RuntimeException("Not fully loaded proxy can not be serialized.");
throw new \RuntimeException("Not fully loaded proxy can not be serialized.");
}
<sleepImpl>
}
......@@ -252,7 +256,7 @@ namespace Doctrine\Generated\Proxies {
public function __sleep() {
if (!$this->_loaded) {
throw new RuntimeException("Not fully loaded proxy can not be serialized.");
throw new \RuntimeException("Not fully loaded proxy can not be serialized.");
}
<sleepImpl>
}
......
......@@ -75,6 +75,8 @@ final class Query extends AbstractQuery
*/
const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
//const HINT_READ_ONLY = 'doctrine.readOnly';
/**
* @var integer $_state The current state of this query.
*/
......
......@@ -21,17 +21,13 @@
namespace Doctrine\ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\DoctrineException;
use Doctrine\Common\PropertyChangedListener;
use Doctrine\ORM\Events;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Internal\CommitOrderCalculator;
use Doctrine\ORM\Internal\CommitOrderNode;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Mapping;
use Doctrine\ORM\Persisters;
use Doctrine\ORM\EntityManager;
use Doctrine\Common\Collections\ArrayCollection,
Doctrine\Common\Collections\ICollection,
Doctrine\Common\DoctrineException,
Doctrine\Common\PropertyChangedListener,
Doctrine\ORM\Event\LifecycleEventArgs,
Doctrine\ORM\Internal\CommitOrderCalculator,
Doctrine\ORM\Internal\CommitOrderNode;
/**
* The UnitOfWork is responsible for tracking changes to objects during an
......@@ -468,13 +464,13 @@ class UnitOfWork implements PropertyChangedListener
if ($class->isCollectionValuedAssociation($name) && $actualData[$name] !== null
&& ! ($actualData[$name] instanceof PersistentCollection)) {
// If $actualData[$name] is Collection then unwrap the array
if ($actualData[$name] instanceof Collection) {
$actualData[$name] = $actualData[$name]->unwrap();
if ( ! $actualData[$name] instanceof ArrayCollection) {
$actualData[$name] = new ArrayCollection($actualData[$name]);
}
$assoc = $class->associationMappings[$name];
// Inject PersistentCollection
$coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName),
$actualData[$name] ? $actualData[$name] : array());
$coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata(
$assoc->targetEntityName), $actualData[$name]);
$coll->setOwner($entity, $assoc);
$coll->setDirty( ! $coll->isEmpty());
$class->reflFields[$name]->setValue($entity, $coll);
......@@ -1326,7 +1322,8 @@ class UnitOfWork implements PropertyChangedListener
} else {
//TODO: Only do this when allowPartialObjects == false?
$coll = new PersistentCollection($this->_em,
$this->_em->getClassMetadata($assoc2->targetEntityName)
$this->_em->getClassMetadata($assoc2->targetEntityName),
new ArrayCollection
);
$coll->setOwner($managedCopy, $assoc2);
$coll->setInitialized($assoc2->isCascadeMerge);
......@@ -1458,7 +1455,7 @@ class UnitOfWork implements PropertyChangedListener
continue;
}
$relatedEntities = $class->reflFields[$assocMapping->sourceFieldName]->getValue($entity);
if ($relatedEntities instanceof Collection) {
if ($relatedEntities instanceof ICollection) {
foreach ($relatedEntities as $relatedEntity) {
$this->_doRefresh($relatedEntity, $visited);
}
......@@ -1482,7 +1479,7 @@ class UnitOfWork implements PropertyChangedListener
continue;
}
$relatedEntities = $class->reflFields[$assocMapping->sourceFieldName]->getValue($entity);
if ($relatedEntities instanceof Collection) {
if ($relatedEntities instanceof ICollection) {
foreach ($relatedEntities as $relatedEntity) {
$this->_doDetach($relatedEntity, $visited);
}
......@@ -1507,7 +1504,7 @@ class UnitOfWork implements PropertyChangedListener
continue;
}
$relatedEntities = $class->reflFields[$assocMapping->sourceFieldName]->getValue($entity);
if ($relatedEntities instanceof Collection) {
if ($relatedEntities instanceof ICollection) {
foreach ($relatedEntities as $relatedEntity) {
$this->_doMerge($relatedEntity, $visited, $managedCopy, $assocMapping);
}
......@@ -1532,7 +1529,7 @@ class UnitOfWork implements PropertyChangedListener
continue;
}
$relatedEntities = $class->reflFields[$assocMapping->sourceFieldName]->getValue($entity);
if (($relatedEntities instanceof Collection || is_array($relatedEntities))) {
if (($relatedEntities instanceof ICollection || is_array($relatedEntities))) {
foreach ($relatedEntities as $relatedEntity) {
$this->_doPersist($relatedEntity, $visited);
}
......@@ -1557,7 +1554,7 @@ class UnitOfWork implements PropertyChangedListener
}
$relatedEntities = $class->reflFields[$assocMapping->sourceFieldName]
->getValue($entity);
if ($relatedEntities instanceof Collection || is_array($relatedEntities)) {
if ($relatedEntities instanceof ICollection || is_array($relatedEntities)) {
foreach ($relatedEntities as $relatedEntity) {
$this->_doRemove($relatedEntity, $visited);
}
......
......@@ -12,7 +12,7 @@ class CollectionTest extends \Doctrine\Tests\DoctrineTestCase
protected function setUp()
{
$this->_coll = new \Doctrine\Common\Collections\Collection;
$this->_coll = new \Doctrine\Common\Collections\ArrayCollection;
}
public function testIssetAndUnset()
......@@ -114,11 +114,11 @@ class CollectionTest extends \Doctrine\Tests\DoctrineTestCase
$this->assertEquals(array(0, 1), $this->_coll->getKeys());
}
public function testGetElements()
public function testGetValues()
{
$this->_coll[] = 'one';
$this->_coll[] = 'two';
$this->assertEquals(array('one', 'two'), $this->_coll->getElements());
$this->assertEquals(array('one', 'two'), $this->_coll->getValues());
}
public function testCount()
......
......@@ -2,7 +2,7 @@
namespace Doctrine\Tests\Models\CMS;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @Entity
......@@ -49,9 +49,9 @@ class CmsUser
public $groups;
public function __construct() {
$this->phonenumbers = new Collection;
$this->articles = new Collection;
$this->groups = new Collection;
$this->phonenumbers = new ArrayCollection;
$this->articles = new ArrayCollection;
$this->groups = new ArrayCollection;
}
public function getId() {
......
......@@ -62,7 +62,7 @@ class CompanyPerson
public function addFriend(CompanyPerson $friend) {
if ( ! $this->friends) {
$this->friends = new \Doctrine\Common\Collections\Collection;
$this->friends = new \Doctrine\Common\Collections\ArrayCollection;
}
if ( ! $this->friends->contains($friend)) {
$this->friends->add($friend);
......
......@@ -2,7 +2,7 @@
namespace Doctrine\Tests\Models\ECommerce;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
/**
* ECommerceCart
......@@ -42,7 +42,7 @@ class ECommerceCart
public function __construct()
{
$this->products = new Collection;
$this->products = new ArrayCollection;
}
public function getId() {
......
......@@ -2,7 +2,7 @@
namespace Doctrine\Tests\Models\ECommerce;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
/**
* ECommerceCategory
......@@ -44,8 +44,8 @@ class ECommerceCategory
public function __construct()
{
$this->products = new Collection();
$this->children = new Collection();
$this->products = new ArrayCollection();
$this->children = new ArrayCollection();
}
public function getId()
......
......@@ -2,7 +2,7 @@
namespace Doctrine\Tests\Models\ECommerce;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
/**
* ECommerceProduct
......@@ -57,9 +57,9 @@ class ECommerceProduct
public function __construct()
{
$this->features = new Collection;
$this->categories = new Collection;
$this->related = new Collection;
$this->features = new ArrayCollection;
$this->categories = new ArrayCollection;
$this->related = new ArrayCollection;
}
public function getId()
......
......@@ -2,7 +2,7 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ICollection;
require_once __DIR__ . '/../../TestInit.php';
......@@ -36,18 +36,8 @@ class AbstractManyToManyAssociationTestCase extends \Doctrine\Tests\OrmFunctiona
->fetchAll());
}
public function assertCollectionEquals(Collection $first, Collection $second)
public function assertCollectionEquals(ICollection $first, ICollection $second)
{
return $first->forAll(function($k, $e) use($second) { return $second->contains($e); });
/*if (count($first) != count($second)) {
return false;
}
foreach ($first as $element) {
if (!$second->contains($element)) {
return false;
}
}
return true;*/
}
}
......@@ -2,7 +2,6 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Collections\Collection;
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
use Doctrine\Tests\Models\ECommerce\ECommerceCategory;
use Doctrine\ORM\Mapping\AssociationMapping;
......
......@@ -2,7 +2,6 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Collections\Collection;
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
use Doctrine\ORM\Mapping\AssociationMapping;
......
......@@ -2,7 +2,6 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Collections\Collection;
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
use Doctrine\ORM\Mapping\AssociationMapping;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment