From 2ec4cc5cbe7a4fdc58cca00c89e154d773b6669a Mon Sep 17 00:00:00 2001 From: romanb Date: Wed, 29 Jul 2009 11:57:27 +0000 Subject: [PATCH] [2.0] More cleanups for recent lazy-loading implementation and minor object hydration improvements and cleanups. Collection refactoring part I for ticket #2352. --- .../{Collection.php => ArrayCollection.php} | 25 +-- .../Internal/Hydration/AbstractHydrator.php | 8 +- .../ORM/Internal/Hydration/ObjectHydrator.php | 193 +++++++----------- .../ORM/Mapping/ClassMetadataFactory.php | 5 + lib/Doctrine/ORM/PersistentCollection.php | 145 +++++++++---- .../Persisters/StandardEntityPersister.php | 4 +- .../ORM/Proxy/ProxyClassGenerator.php | 14 +- lib/Doctrine/ORM/Query.php | 2 + lib/Doctrine/ORM/UnitOfWork.php | 39 ++-- .../Common/Collections/CollectionTest.php | 6 +- tests/Doctrine/Tests/Models/CMS/CmsUser.php | 8 +- .../Tests/Models/Company/CompanyPerson.php | 2 +- .../Tests/Models/ECommerce/ECommerceCart.php | 4 +- .../Models/ECommerce/ECommerceCategory.php | 6 +- .../Models/ECommerce/ECommerceProduct.php | 8 +- .../AbstractManyToManyAssociationTestCase.php | 14 +- ...ManyToManyBidirectionalAssociationTest.php | 1 - ...nyToManySelfReferentialAssociationTest.php | 1 - ...anyToManyUnidirectionalAssociationTest.php | 1 - 19 files changed, 252 insertions(+), 234 deletions(-) rename lib/Doctrine/Common/Collections/{Collection.php => ArrayCollection.php} (95%) diff --git a/lib/Doctrine/Common/Collections/Collection.php b/lib/Doctrine/Common/Collections/ArrayCollection.php similarity index 95% rename from lib/Doctrine/Common/Collections/Collection.php rename to lib/Doctrine/Common/Collections/ArrayCollection.php index e819bd577..d68cd8eba 100644 --- a/lib/Doctrine/Common/Collections/Collection.php +++ b/lib/Doctrine/Common/Collections/ArrayCollection.php @@ -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 * @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 */ @@ -63,6 +59,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)); } /** diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index e3b56e255..8c70f4481 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -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."); } /** diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 7cddfd258..d63f4395d 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -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; - } - - /** - * - * @param $entity - * @param $assocField - * @param $indexField - * @return - * @todo Inline this method. - */ - private function isIndexKeyInUse($entity, $assocField, $indexField) - { - return $this->_ce[get_class($entity)] - ->reflFields[$assocField] - ->getValue($entity) - ->containsKey($indexField); + $this->_initializedCollections[$oid . $name] = $pColl; + + 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; } } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 95d73da91..fc1e5f6b9 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -111,6 +111,11 @@ class ClassMetadataFactory } return $this->_loadedMetadata[$className]; } + + public function hasMetadataFor($className) + { + return isset($this->_loadedMetadata[$className]); + } /** * Sets the metadata descriptor for a specific class. diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 729837b3c..101631ce7 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -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 * @author Giorgio Sironi */ -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; } } diff --git a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php index 5664eafce..ef5f12a11 100644 --- a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php @@ -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()) { diff --git a/lib/Doctrine/ORM/Proxy/ProxyClassGenerator.php b/lib/Doctrine/ORM/Proxy/ProxyClassGenerator.php index ed35d2569..078aa6c98 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyClassGenerator.php +++ b/lib/Doctrine/ORM/Proxy/ProxyClassGenerator.php @@ -20,6 +20,7 @@ */ namespace Doctrine\ORM\Proxy; + use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\ClassMetadata; @@ -85,12 +86,15 @@ 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); - + if (class_exists($proxyFullyQualifiedClassName, false)) { return $proxyFullyQualifiedClassName; } @@ -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."); } } @@ -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."); } } diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 6b19a8412..d6ab24cec 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -74,6 +74,8 @@ final class Query extends AbstractQuery * @var string */ const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns'; + + //const HINT_READ_ONLY = 'doctrine.readOnly'; /** * @var integer $_state The current state of this query. diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 72018765f..fa2a647ab 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -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); } diff --git a/tests/Doctrine/Tests/Common/Collections/CollectionTest.php b/tests/Doctrine/Tests/Common/Collections/CollectionTest.php index c6049e007..ff4fdc490 100644 --- a/tests/Doctrine/Tests/Common/Collections/CollectionTest.php +++ b/tests/Doctrine/Tests/Common/Collections/CollectionTest.php @@ -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() diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUser.php b/tests/Doctrine/Tests/Models/CMS/CmsUser.php index a8f2b15e6..db72d7551 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUser.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUser.php @@ -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() { diff --git a/tests/Doctrine/Tests/Models/Company/CompanyPerson.php b/tests/Doctrine/Tests/Models/Company/CompanyPerson.php index 06b5a1dd6..21395dc88 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyPerson.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyPerson.php @@ -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); diff --git a/tests/Doctrine/Tests/Models/ECommerce/ECommerceCart.php b/tests/Doctrine/Tests/Models/ECommerce/ECommerceCart.php index 7c240e8e2..8fac69be0 100644 --- a/tests/Doctrine/Tests/Models/ECommerce/ECommerceCart.php +++ b/tests/Doctrine/Tests/Models/ECommerce/ECommerceCart.php @@ -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() { diff --git a/tests/Doctrine/Tests/Models/ECommerce/ECommerceCategory.php b/tests/Doctrine/Tests/Models/ECommerce/ECommerceCategory.php index 5000c051f..ea1d9196e 100644 --- a/tests/Doctrine/Tests/Models/ECommerce/ECommerceCategory.php +++ b/tests/Doctrine/Tests/Models/ECommerce/ECommerceCategory.php @@ -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() diff --git a/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php b/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php index e3abb5c03..f4f1432a0 100644 --- a/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php +++ b/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php @@ -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() diff --git a/tests/Doctrine/Tests/ORM/Functional/AbstractManyToManyAssociationTestCase.php b/tests/Doctrine/Tests/ORM/Functional/AbstractManyToManyAssociationTestCase.php index bbfa6b9a3..21f97b696 100644 --- a/tests/Doctrine/Tests/ORM/Functional/AbstractManyToManyAssociationTestCase.php +++ b/tests/Doctrine/Tests/ORM/Functional/AbstractManyToManyAssociationTestCase.php @@ -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;*/ } } diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php index 951bf5aa0..0b9833c21 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBidirectionalAssociationTest.php @@ -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; diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php index 6706a9d9a..7286bfcf3 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManySelfReferentialAssociationTest.php @@ -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; diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php index f4968b0b7..148362162 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyUnidirectionalAssociationTest.php @@ -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;