From 36763dadb62512c274438c4adb1545e0817b5601 Mon Sep 17 00:00:00 2001 From: romanb Date: Thu, 29 Jan 2009 17:00:44 +0000 Subject: [PATCH] A little progress on the UnitOfWork. --- .../Common/Collections/Collection.php | 21 ++- lib/Doctrine/DBAL/Connection.php | 3 +- lib/Doctrine/ORM/ActiveEntity.php | 1 + lib/Doctrine/ORM/Collection.php | 59 +++---- .../ORM/Internal/Hydration/ObjectHydrator.php | 6 +- .../AbstractCollectionPersister.php | 38 +++-- .../Persisters/AbstractEntityPersister.php | 37 +---- lib/Doctrine/ORM/UnitOfWork.php | 144 +++++++++++------- tests/Doctrine/Tests/Models/CMS/CmsUser.php | 12 +- .../Tests/ORM/Functional/BasicCRUDTest.php | 22 ++- .../ORM/Hydration/ObjectHydratorTest.php | 2 - tests/Doctrine/Tests/ORM/UnitOfWorkTest.php | 2 +- 12 files changed, 196 insertions(+), 151 deletions(-) diff --git a/lib/Doctrine/Common/Collections/Collection.php b/lib/Doctrine/Common/Collections/Collection.php index a6b925f8f..a17e08fe8 100644 --- a/lib/Doctrine/Common/Collections/Collection.php +++ b/lib/Doctrine/Common/Collections/Collection.php @@ -9,6 +9,7 @@ namespace Doctrine\Common\Collections; use \Countable; use \IteratorAggregate; use \ArrayAccess; +use \ArrayIterator; /** * A Collection is a wrapper around a php array and just like a php array a @@ -88,6 +89,22 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess return $removed; } + /** + * Removes the specified element from the collection, if it is found. + * + * @param mixed $element + * @return boolean + */ + public function removeElement($element) + { + $key = array_search($element, $this->_data, true); + if ($key !== false) { + unset($this->_data[$key]); + return true; + } + return false; + } + /** * @see containsKey() */ @@ -174,7 +191,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess * Tests for the existance of an element that satisfies the given predicate. * * @param function $func - * @return boolean TRUE if the predicate is TRUE for at least one element, FALSe otherwise. + * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise. */ public function exists(Closure $func) { foreach ($this->_data as $key => $element) @@ -191,7 +208,7 @@ class Collection implements Countable, IteratorAggregate, ArrayAccess */ public function containsAll($otherColl) { - throw new Doctrine_Exception("Not yet implemented."); + throw new DoctrineException("Not yet implemented."); } /** diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index 6971526c9..dec5157c4 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -495,7 +495,7 @@ class Connection */ public function prepare($statement) { - echo $statement; + echo $statement . PHP_EOL; $this->connect(); try { return $this->_conn->prepare($statement); @@ -561,6 +561,7 @@ class Connection $this->connect(); try { if ( ! empty($params)) { + //var_dump($params); $stmt = $this->prepare($query); $stmt->execute($params); return $stmt->rowCount(); diff --git a/lib/Doctrine/ORM/ActiveEntity.php b/lib/Doctrine/ORM/ActiveEntity.php index 89c6b9b36..e0efa16bd 100644 --- a/lib/Doctrine/ORM/ActiveEntity.php +++ b/lib/Doctrine/ORM/ActiveEntity.php @@ -9,6 +9,7 @@ * most method calls to the EntityManager. * * @since 2.0 + * @todo Any takers for this one? Needs a rewrite. */ class Doctrine_ORM_ActiveEntity { diff --git a/lib/Doctrine/ORM/Collection.php b/lib/Doctrine/ORM/Collection.php index 2ed39d4dc..0254b8456 100644 --- a/lib/Doctrine/ORM/Collection.php +++ b/lib/Doctrine/ORM/Collection.php @@ -21,11 +21,13 @@ namespace Doctrine\ORM; +use Doctrine\ORM\Mapping\AssociationMapping; + /** * A persistent collection wrapper. * - * A PersistentCollection represents a collection of entities. Collections of - * entities represent only the associations (links) to those entities. + * A PersistentCollection represents a collection of elements that have persistent state. + * Collections of entities represent only the associations (links) to those entities. * That means, if the collection is part of a many-many mapping and you remove * entities from the collection, only the links in the xref table are removed (on flush). * Similarly, if you remove entities from a collection that is part of a one-many @@ -104,13 +106,17 @@ final class Collection extends \Doctrine\Common\Collections\Collection */ private $_hydrationFlag; + /** + * The class descriptor of the owning entity. + */ private $_ownerClass; /** * Creates a new persistent collection. */ - public function __construct(EntityManager $em, $entityBaseType, $keyField = null) + public function __construct(EntityManager $em, $entityBaseType, array $data = array(), $keyField = null) { + parent::__construct($data); $this->_entityBaseType = $entityBaseType; $this->_em = $em; $this->_ownerClass = $em->getClassMetadata($entityBaseType); @@ -151,7 +157,7 @@ final class Collection extends \Doctrine\Common\Collections\Collection * @param object $entity * @param AssociationMapping $relation */ - public function _setOwner($entity, \Doctrine\ORM\Mapping\AssociationMapping $relation) + public function _setOwner($entity, AssociationMapping $relation) { $this->_owner = $entity; $this->_association = $relation; @@ -180,7 +186,7 @@ final class Collection extends \Doctrine\Common\Collections\Collection } /** - * Removes an entity from the collection. + * Removes an element from the collection. * * @param mixed $key * @return boolean @@ -215,23 +221,23 @@ final class Collection extends \Doctrine\Common\Collections\Collection } /** - * Adds an entry to the collection. + * Adds an element to the collection. * * @param mixed $value * @param string $key - * @return boolean + * @return TRUE * @override */ public function add($value) { $result = parent::add($value); if ( ! $result) return $result; // EARLY EXIT - + if ($this->_hydrationFlag) { if ($this->_backRefFieldName) { // set back reference to owner - $this->_ownerClass->getReflectionProperty( - $this->_backRefFieldName)->setValue($value, $this->_owner); + $this->_ownerClass->getReflectionProperty($this->_backRefFieldName) + ->setValue($value, $this->_owner); } } else { //TODO: Register collection as dirty with the UoW if necessary @@ -295,27 +301,6 @@ final class Collection extends \Doctrine\Common\Collections\Collection return $this->_snapshot; } - /** - * INTERNAL: - * Processes the difference of the last snapshot and the current data. - * - * an example: - * Snapshot with the objects 1, 2 and 4 - * Current data with objects 2, 3 and 5 - * - * The process would remove objects 1 and 4 - * - * @return Doctrine_Collection - * @todo Move elsewhere - */ - public function processDiff() - { - foreach (array_udiff($this->_snapshot, $this->_data, array($this, "_compareRecords")) as $record) { - $record->delete(); - } - return $this; - } - /** * INTERNAL: * getDeleteDiff @@ -324,7 +309,7 @@ final class Collection extends \Doctrine\Common\Collections\Collection */ public function getDeleteDiff() { - return array_udiff($this->_snapshot, $this->_data, array($this, "_compareRecords")); + return array_udiff($this->_snapshot, $this->_data, array($this, '_compareRecords')); } /** @@ -334,7 +319,7 @@ final class Collection extends \Doctrine\Common\Collections\Collection */ public function getInsertDiff() { - return array_udiff($this->_data, $this->_snapshot, array($this, "_compareRecords")); + return array_udiff($this->_data, $this->_snapshot, array($this, '_compareRecords')); } /** @@ -377,8 +362,10 @@ final class Collection extends \Doctrine\Common\Collections\Collection private function _changed() { - /*if ( ! $this->_em->getUnitOfWork()->isCollectionScheduledForUpdate($this)) { - $this->_em->getUnitOfWork()->scheduleCollectionUpdate($this); - }*/ + if ( ! $this->_em->getUnitOfWork()->isCollectionScheduledForUpdate($this)) { + //var_dump(get_class($this->_snapshot[0])); + //echo "NOT!"; + //$this->_em->getUnitOfWork()->scheduleCollectionUpdate($this); + } } } diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 34512d24f..26b4c1d07 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -54,7 +54,7 @@ class ObjectHydrator extends AbstractHydrator if ($this->_parserResult->isMixedQuery()) { $result = array(); } else { - $result = new \Doctrine\ORM\Collection($this->_em, $this->_rootEntityName); + $result = new \Doctrine\Common\Collections\Collection; } $cache = array(); @@ -66,7 +66,7 @@ class ObjectHydrator extends AbstractHydrator foreach ($this->_collections as $coll) { $coll->_takeSnapshot(); $coll->_setHydrationFlag(false); - $this->_uow->addManagedCollection($coll); + //$this->_uow->addManagedCollection($coll); } // Clean up @@ -105,7 +105,7 @@ class ObjectHydrator extends AbstractHydrator if ( ! is_object($coll)) { end($coll); $this->_resultPointers[$dqlAlias] =& $coll[key($coll)]; - } else if ($coll instanceof \Doctrine\ORM\Collection) { + } else if ($coll instanceof \Doctrine\Common\Collections\Collection) { if (count($coll) > 0) { $this->_resultPointers[$dqlAlias] = $coll->last(); } diff --git a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php index ee940952d..e7c564e17 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractCollectionPersister.php @@ -1,6 +1,10 @@ getRelation()->isInverseSide()) { return; } - //... } @@ -17,32 +20,39 @@ class Doctrine_ORM_Persisters_AbstractCollectionPersister if ($coll->getRelation()->isInverseSide()) { return; } - //... - if ($coll->getRelation() instanceof Doctrine_Association_OneToManyMapping) { - //... - } else if ($coll->getRelation() instanceof Doctrine_Association_ManyToManyMapping) { - //... - } } /* collection update actions */ - public function deleteRows() + public function deleteRows(Collection $coll) + { + //$collection->getDeleteDiff(); + } + + public function updateRows(Collection $coll) { } - public function updateRows() + public function insertRows(Collection $coll) + { + //$collection->getInsertDiff(); + } + + protected function _getDeleteRowSql() { } - - public function insertRows() + + protected function _getUpdateRowSql() + { + + } + + protected function _getDeleteRowSql() { } - } -?> \ No newline at end of file diff --git a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php index 7a79a8bce..b164399dc 100644 --- a/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/AbstractEntityPersister.php @@ -31,14 +31,9 @@ namespace Doctrine\ORM\Persisters; * @since 2.0 */ abstract class AbstractEntityPersister -{ +{ /** - * The names of all the fields that are available on entities. - */ - protected $_fieldNames = array(); - - /** - * Metadata object that descibes the mapping of the mapped entity class. + * Metadata object that describes the mapping of the mapped entity class. * * @var Doctrine\ORM\Mapping\ClassMetadata */ @@ -124,24 +119,12 @@ abstract class AbstractEntityPersister /** * - * @return + * @return Doctrine\ORM\ClassMetadata */ public function getClassMetadata() { return $this->_classMetadata; } - - /** - * @todo Move to ClassMetadata? - */ - public function getFieldNames() - { - if ($this->_fieldNames) { - return $this->_fieldNames; - } - $this->_fieldNames = $this->_classMetadata->getFieldNames(); - return $this->_fieldNames; - } /** * Gets the name of the class in the entity hierarchy that owns the field with @@ -156,15 +139,10 @@ abstract class AbstractEntityPersister if ($this->_classMetadata->isInheritanceTypeNone()) { return $this->_classMetadata; } else { - foreach ($this->_classMetadata->getParentClasses() as $parentClass) { - $parentClassMetadata = Doctrine_ORM_Mapping_ClassMetadataFactory::getInstance() - ->getMetadataFor($parentClass); - if ( ! $parentClassMetadata->isInheritedField($fieldName)) { - return $parentClassMetadata; - } - } + $mapping = $this->_classMetadata->getFieldMapping($fieldName); + return $mapping['inherited']; } - throw new Doctrine_Exception("Unable to find defining class of field '$fieldName'."); + throw new DoctrineException("Unable to find defining class of field '$fieldName'."); } /** @@ -186,8 +164,9 @@ abstract class AbstractEntityPersister /** * Prepares all the entity data for insertion into the database. * + * @param object $entity * @param array $array - * @return void + * @param boolean $isInsert */ protected function _prepareData($entity, array &$result, $isInsert = false) { diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 326771d92..4ea2c1b46 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -23,6 +23,7 @@ namespace Doctrine\ORM; use Doctrine\ORM\Internal\CommitOrderCalculator; use Doctrine\ORM\Internal\CommitOrderNode; +use Doctrine\ORM\Collection; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Exceptions\UnitOfWorkException; @@ -110,6 +111,12 @@ class UnitOfWork */ protected $_entityStates = array(); + /** + * Map of entities that are scheduled for dirty checking at commit time. + * This is only used if automatic dirty checking is disabled. + */ + protected $_scheduledForDirtyCheck = array(); + /** * A list of all new entities that need to be INSERTed. * @@ -186,15 +193,19 @@ class UnitOfWork } /** - * Commits the unit of work, executing all operations that have been postponed + * Commits the UnitOfWork, executing all operations that have been postponed * up to this point. - * - * @return void */ public function commit() { // Compute changes in managed entities - $this->computeEntityChangeSets(); + $this->computeChangeSets(); + + /*foreach ($this->_managedCollections as $coll) { + if ($coll->isDirty()) { + + } + }*/ if (empty($this->_newEntities) && empty($this->_deletedEntities) && @@ -214,18 +225,18 @@ class UnitOfWork $this->_executeUpdates($class); } - //TODO: collection deletions - //TODO: collection updates (deleteRows, updateRows, insertRows) - //TODO: collection recreations + //TODO: collection deletions (deletions of complete collections) + //TODO: collection updates (deleteRows, updateRows, insertRows on join tables) + //TODO: collection recreations (insertions of complete collections) // Entity deletions come last and need to be in reverse commit order - for ($count = count($commitOrder), $i = $count - 1; $i >= 0; $i--) { + for ($count = count($commitOrder), $i = $count - 1; $i >= 0; --$i) { $this->_executeDeletions($commitOrder[$i]); } //TODO: commit transaction here? - // clear up + // Clear up $this->_newEntities = array(); $this->_dirtyEntities = array(); $this->_deletedEntities = array(); @@ -253,9 +264,11 @@ class UnitOfWork * * @param array $entities The entities for which to compute the changesets. If this * parameter is not specified, the changesets of all entities in the identity - * map are computed. + * map are computed if automatic dirty checking is enabled (the default). + * If automatic dirty checking is disabled, only those changesets will be + * computed that have been scheduled through scheduleForDirtyCheck(). */ - public function computeEntityChangeSets(array $entities = null) + public function computeChangeSets(array $entities = null) { $entitySet = array(); if ( ! is_null($entities)) { @@ -263,21 +276,20 @@ class UnitOfWork $entitySet[get_class($entity)][] = $entity; } } else if ( ! $this->_em->getConfiguration()->getAutomaticDirtyChecking()) { - //TODO + $entitySet = $this->_scheduledForDirtyCheck; } else { $entitySet = $this->_identityMap; } foreach ($entitySet as $className => $entities) { $class = $this->_em->getClassMetadata($className); + if ( ! $class->isInheritanceTypeNone() && count($entities) > 0) { + $class = $this->_em->getClassMetadata(get_class($entities[0])); + } foreach ($entities as $entity) { $oid = spl_object_hash($entity); $state = $this->getEntityState($entity); if ($state == self::STATE_MANAGED || $state == self::STATE_NEW) { - if ( ! $class->isInheritanceTypeNone()) { - $class = $this->_em->getClassMetadata(get_class($entity)); - } - $actualData = array(); foreach ($class->getReflectionProperties() as $name => $refProp) { $actualData[$name] = $refProp->getValue($entity); @@ -294,6 +306,7 @@ class UnitOfWork $originalData = $this->_originalEntityData[$oid]; $changeSet = array(); $entityIsDirty = false; + foreach ($actualData as $propName => $actualValue) { $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if (is_object($orgValue) && $orgValue !== $actualValue) { @@ -307,8 +320,16 @@ class UnitOfWork $assoc = $class->getAssociationMapping($propName); if ($assoc->isOneToOne() && $assoc->isOwningSide()) { $entityIsDirty = true; + } else if ( ! $assoc->isOneToOne()) { + if ( ! $actualValue instanceof Collection) { + // Inject PersistentCollection + $coll = new Collection($this->_em, $assoc->getTargetEntityName(), $actualValue); + //$coll->_takeSnapshot(); + $class->getReflectionProperty($propName)->setValue($entity, $coll); + $actualData[$propName] = $coll; + } } - $this->_handleAssociationValueChanged($assoc, $actualValue); + //$this->_handleAssociationValueChanged($assoc, $actualValue); } else { $entityIsDirty = true; } @@ -322,6 +343,16 @@ class UnitOfWork $this->_originalEntityData[$oid] = $actualData; } } + + // Look for changes in associations + if ($state == self::STATE_MANAGED) { + foreach ($class->getAssociationMappings() as $assoc) { + $val = $actualData[$assoc->getSourceFieldName()]; + if ( ! is_null($val)) { + $this->_computeAssociationChanges($assoc, $val); + } + } + } } } } @@ -336,7 +367,7 @@ class UnitOfWork * @param $assoc * @param $value */ - private function _handleAssociationValueChanged($assoc, $value) + private function _computeAssociationChanges($assoc, $value) { if ($assoc->isOneToOne()) { $value = array($value); @@ -376,7 +407,6 @@ class UnitOfWork // MANAGED associated entities are already taken into account // during changeset calculation anyway, since they are in the identity map. } - } /** @@ -396,6 +426,7 @@ class UnitOfWork if (get_class($entity) == $className) { $returnVal = $persister->insert($entity); if ( ! is_null($returnVal)) { + // Persister returned a post-insert ID $oid = spl_object_hash($entity); $idField = $class->getSingleIdentifierFieldName(); $class->getReflectionProperty($idField)->setValue($entity, $returnVal); @@ -453,11 +484,6 @@ class UnitOfWork $this->_dirtyEntities, $this->_deletedEntities); } - - /* if (count($entityChangeSet) == 1) { - * return array($entityChangeSet[0]->getClass()); - * } - */ // TODO: We can cache computed commit orders in the metadata cache! // Check cache at this point here! @@ -830,7 +856,7 @@ class UnitOfWork /** * Saves an entity as part of the current unit of work. * - * @param Doctrine\ORM\Entity $entity The entity to save. + * @param object $entity The entity to save. */ public function save($entity) { @@ -839,8 +865,8 @@ class UnitOfWork $this->_doSave($entity, $visited, $insertNow); if ( ! empty($insertNow)) { // We have no choice. This means that there are new entities - // with an IDENTITY column key generation strategy. - $this->computeEntityChangeSets($insertNow); + // with a post-insert ID generation strategy. + $this->computeChangeSets($insertNow); $commitOrder = $this->_getCommitOrder($insertNow); foreach ($commitOrder as $class) { $this->_executeInserts($class); @@ -858,6 +884,8 @@ class UnitOfWork * * @param object $entity The entity to save. * @param array $visited The already visited entities. + * @param array $insertNow The entities that must be immediately inserted because of + * post-insert ID generation. */ private function _doSave($entity, array &$visited, array &$insertNow) { @@ -871,7 +899,10 @@ class UnitOfWork $class = $this->_em->getClassMetadata(get_class($entity)); switch ($this->getEntityState($entity)) { case self::STATE_MANAGED: - // nothing to do + // nothing to do, except if automatic dirty checking is disabled + if ( ! $this->_em->getConfiguration()->getAutomaticDirtyChecking()) { + $this->scheduleForDirtyCheck($entity); + } break; case self::STATE_NEW: $idGen = $this->_em->getIdGenerator($class->getClassName()); @@ -907,7 +938,6 @@ class UnitOfWork //TODO: throw UnitOfWorkException::invalidEntityState() throw new Doctrine_Exception("Encountered invalid entity state."); } - $this->_cascadeSave($entity, $visited, $insertNow); } @@ -923,9 +953,11 @@ class UnitOfWork } /** - * Enter description here... + * Deletes an entity as part of the current unit of work. + * This method is internally called during delete() cascades as it tracks + * the already visited entities to prevent infinite recursions. * - * @param Doctrine\ORM\Entity $entity + * @param object $entity * @param array $visited */ private function _doDelete($entity, array &$visited) @@ -940,26 +972,23 @@ class UnitOfWork switch ($this->getEntityState($entity)) { case self::STATE_NEW: case self::STATE_DELETED: - // nothing to do for $entity + // nothing to do break; case self::STATE_MANAGED: $this->registerDeleted($entity); break; case self::STATE_DETACHED: - //exception? throw new DoctrineException("A detached entity can't be deleted."); default: - //TODO: throw UnitOfWorkException::invalidEntityState() throw new DoctrineException("Encountered invalid entity state."); } - $this->_cascadeDelete($entity, $visited); } /** * Cascades the save operation to associated entities. * - * @param Doctrine\ORM\Entity $entity + * @param object $entity * @param array $visited */ private function _cascadeSave($entity, array &$visited, array &$insertNow) @@ -971,7 +1000,7 @@ class UnitOfWork } $relatedEntities = $class->getReflectionProperty($assocMapping->getSourceFieldName()) ->getValue($entity); - if (($relatedEntities instanceof Doctrine_ORM_Collection || is_array($relatedEntities)) + if (($relatedEntities instanceof \Doctrine\Common\Collections\Collection || is_array($relatedEntities)) && count($relatedEntities) > 0) { foreach ($relatedEntities as $relatedEntity) { $this->_doSave($relatedEntity, $visited, $insertNow); @@ -985,9 +1014,9 @@ class UnitOfWork /** * Cascades the delete operation to associated entities. * - * @param Doctrine\ORM\Entity $entity + * @param object $entity */ - private function _cascadeDelete($entity) + private function _cascadeDelete($entity, array &$visited) { $class = $this->_em->getClassMetadata(get_class($entity)); foreach ($class->getAssociationMappings() as $assocMapping) { @@ -996,13 +1025,13 @@ class UnitOfWork } $relatedEntities = $class->getReflectionProperty($assocMapping->getSourceFieldName()) ->getValue($entity); - if ($relatedEntities instanceof \Doctrine\ORM\Collection && + if ($relatedEntities instanceof \Doctrine\Common\Collections\Collection && count($relatedEntities) > 0) { foreach ($relatedEntities as $relatedEntity) { - $this->_doDelete($relatedEntity, $visited, $insertNow); + $this->_doDelete($relatedEntity, $visited); } } else if (is_object($relatedEntities)) { - $this->_doDelete($relatedEntities, $visited, $insertNow); + $this->_doDelete($relatedEntities, $visited); } } } @@ -1026,34 +1055,34 @@ class UnitOfWork $this->_commitOrderCalculator->clear(); } - public function scheduleCollectionUpdate(Doctrine\ORM\Collection $coll) + public function scheduleCollectionUpdate(Collection $coll) { $this->_collectionUpdates[] = $coll; } - public function isCollectionScheduledForUpdate(Doctrine\ORM\Collection $coll) + public function isCollectionScheduledForUpdate(Collection $coll) { //... } - public function scheduleCollectionDeletion(Doctrine\ORM\Collection $coll) + public function scheduleCollectionDeletion(Collection $coll) { //TODO: if $coll is already scheduled for recreation ... what to do? // Just remove $coll from the scheduled recreations? $this->_collectionDeletions[] = $coll; } - public function isCollectionScheduledForDeletion(Doctrine\ORM\Collection $coll) + public function isCollectionScheduledForDeletion(Collection $coll) { //... } - public function scheduleCollectionRecreation(Doctrine\ORM\Collection $coll) + public function scheduleCollectionRecreation(Collection $coll) { $this->_collectionRecreations[] = $coll; } - public function isCollectionScheduledForRecreation(Doctrine\ORM\Collection $coll) + public function isCollectionScheduledForRecreation(Collection $coll) { //... } @@ -1063,7 +1092,7 @@ class UnitOfWork * * @param string $className The name of the entity class. * @param array $data The data for the entity. - * @return Doctrine\ORM\Entity + * @return object * @internal Performance-sensitive method. */ public function createEntity($className, array $data, $query = null) @@ -1104,7 +1133,7 @@ class UnitOfWork * Merges the given data into the given entity, optionally overriding * local changes. * - * @param Doctrine\ORM\Entity $entity + * @param object $entity * @param array $data * @param boolean $overrideLocalChanges */ @@ -1137,14 +1166,11 @@ class UnitOfWork private function _inferCorrectClassName(array $data, $className) { $class = $this->_em->getClassMetadata($className); - $discCol = $class->getDiscriminatorColumn(); if ( ! $discCol) { return $className; } - $discMap = $class->getDiscriminatorMap(); - if (isset($data[$discCol['name']], $discMap[$data[$discCol['name']]])) { return $discMap[$data[$discCol['name']]]; } else { @@ -1203,10 +1229,10 @@ class UnitOfWork * * @param Doctrine\ORM\Collection $coll */ - public function addManagedCollection(\Doctrine\ORM\Collection $coll) + /*public function addManagedCollection(Collection $coll) { - } + }*/ /** * Gets the identifier of an entity. @@ -1234,9 +1260,17 @@ class UnitOfWork return false; } + public function scheduleForDirtyCheck($entity) + { + $rootClassName = $this->_em->getClassMetadata(get_class($entity))->getRootClassName(); + $this->_scheduledForDirtyCheck[$rootClassName] = $entity; + } + /** * Calculates the size of the UnitOfWork. The size of the UnitOfWork is the * number of entities in the identity map. + * + * @return integer */ public function size() { diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUser.php b/tests/Doctrine/Tests/Models/CMS/CmsUser.php index ab65882ff..bdcb0c1bf 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUser.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUser.php @@ -27,7 +27,7 @@ class CmsUser public $name; /** * @DoctrineOneToMany(targetEntity="Doctrine\Tests\Models\CMS\CmsPhonenumber", - mappedBy="user", cascade={"save"}) + mappedBy="user", cascade={"save", "delete"}) */ public $phonenumbers; /** @@ -44,4 +44,14 @@ class CmsUser $this->phonenumbers[] = $phone; $phone->user = $this; } + + public function removePhonenumber($index) { + if (isset($this->phonenumbers[$index])) { + $ph = $this->phonenumbers[$index]; + unset($this->phonenumbers[$index]); + $ph->user = null; + return true; + } + return false; + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php index 34eaa5e4f..4c2dddcfc 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicCRUDTest.php @@ -42,24 +42,32 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase { $em->flush(); $this->assertTrue($em->contains($ph)); $this->assertTrue($em->contains($user)); + $this->assertTrue($user->phonenumbers instanceof \Doctrine\ORM\Collection); - // Update + // Update name $user->name = 'guilherme'; $em->flush(); $this->assertEquals('guilherme', $user->name); + // Add another phonenumber + $ph2 = new CmsPhonenumber; + $ph2->phonenumber = "6789"; + $user->addPhonenumber($ph2); + $em->flush(); + $this->assertTrue($em->contains($ph2)); + // Delete $em->delete($user); $this->assertTrue($em->getUnitOfWork()->isRegisteredRemoved($user)); + $this->assertTrue($em->getUnitOfWork()->isRegisteredRemoved($ph)); + $this->assertTrue($em->getUnitOfWork()->isRegisteredRemoved($ph2)); $em->flush(); $this->assertFalse($em->getUnitOfWork()->isRegisteredRemoved($user)); + $this->assertFalse($em->getUnitOfWork()->isRegisteredRemoved($ph)); + $this->assertFalse($em->getUnitOfWork()->isRegisteredRemoved($ph2)); } - public function testMore() { - #echo PHP_EOL . "SECOND" . PHP_EOL; - /*$user = new CmsUser; - $user->name = 'jon'; - $user->*/ + /*public function testMore() { $ph = new CmsPhonenumber; $ph->phonenumber = 123456; @@ -67,6 +75,6 @@ class BasicCRUDTest extends \Doctrine\Tests\OrmFunctionalTestCase { $this->_em->save($ph); $this->_em->flush(); - } + }*/ } diff --git a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php index 6b3ab7c3c..ca99521d3 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php @@ -53,7 +53,6 @@ class ObjectHydratorTest extends HydrationTest $queryComponents, $tableAliasMap)); $this->assertEquals(2, count($result)); - $this->assertTrue($result instanceof \Doctrine\ORM\Collection); $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\CMS\CmsUser); $this->assertTrue($result[1] instanceof \Doctrine\Tests\Models\CMS\CmsUser); $this->assertEquals(1, $result[0]->id); @@ -659,7 +658,6 @@ class ObjectHydratorTest extends HydrationTest $queryComponents, $tableAliasMap)); $this->assertEquals(2, count($result)); - $this->assertTrue($result instanceof \Doctrine\ORM\Collection); $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\Forum\ForumCategory); $this->assertTrue($result[1] instanceof \Doctrine\Tests\Models\Forum\ForumCategory); $this->assertEquals(1, $result[0]->getId()); diff --git a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php index 052fa0a8c..01db72934 100644 --- a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php +++ b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php @@ -159,7 +159,7 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase )); // Go - $this->_unitOfWork->computeEntityChangeSets(array($user1, $user2)); + $this->_unitOfWork->computeChangeSets(array($user1, $user2)); // Verify $user1ChangeSet = $this->_unitOfWork->getEntityChangeSet($user1);