Commit f064de2a authored by romanb's avatar romanb

[2.0] Fixed issue with self-referential one-to-many associations not being...

[2.0] Fixed issue with self-referential one-to-many associations not being persisted correctly when IDENTITY key generation was used. Included now passing OneToManySelfReferentialTest.
parent 4e70e5d8
...@@ -187,8 +187,8 @@ class JoinedSubclassPersister extends StandardEntityPersister ...@@ -187,8 +187,8 @@ class JoinedSubclassPersister extends StandardEntityPersister
$this->_prepareData($entity, $updateData); $this->_prepareData($entity, $updateData);
$id = array_combine( $id = array_combine(
$this->_class->getIdentifierFieldNames(), $this->_class->getIdentifierFieldNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity) $this->_em->getUnitOfWork()->getEntityIdentifier($entity)
); );
foreach ($updateData as $tableName => $data) { foreach ($updateData as $tableName => $data) {
...@@ -205,15 +205,15 @@ class JoinedSubclassPersister extends StandardEntityPersister ...@@ -205,15 +205,15 @@ class JoinedSubclassPersister extends StandardEntityPersister
public function delete($entity) public function delete($entity)
{ {
$id = array_combine( $id = array_combine(
$this->_class->getIdentifierFieldNames(), $this->_class->getIdentifierFieldNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity) $this->_em->getUnitOfWork()->getEntityIdentifier($entity)
); );
// If the database platform supports FKs, just // If the database platform supports FKs, just
// delete the row from the root table. Cascades do the rest. // delete the row from the root table. Cascades do the rest.
if ($this->_conn->getDatabasePlatform()->supportsForeignKeyConstraints()) { if ($this->_conn->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$this->_conn->delete($this->_em->getClassMetadata($this->_class->rootEntityName) $this->_conn->delete($this->_em->getClassMetadata($this->_class->rootEntityName)
->primaryTable['name'], $id); ->primaryTable['name'], $id);
} else { } else {
// Delete the parent tables, starting from this class' table up to the root table // Delete the parent tables, starting from this class' table up to the root table
$this->_conn->delete($this->_class->primaryTable['name'], $id); $this->_conn->delete($this->_class->primaryTable['name'], $id);
......
...@@ -179,16 +179,18 @@ class StandardEntityPersister ...@@ -179,16 +179,18 @@ class StandardEntityPersister
{ {
$updateData = array(); $updateData = array();
$this->_prepareData($entity, $updateData); $this->_prepareData($entity, $updateData);
$id = array_combine($this->_class->getIdentifierFieldNames(), $id = array_combine(
$this->_em->getUnitOfWork()->getEntityIdentifier($entity)); $this->_class->getIdentifierFieldNames(),
$this->_em->getUnitOfWork()->getEntityIdentifier($entity)
);
$tableName = $this->_class->primaryTable['name']; $tableName = $this->_class->primaryTable['name'];
if ($this->_evm->hasListeners(Events::preUpdate)) { if ($this->_evm->hasListeners(Events::preUpdate)) {
$this->_preUpdate($entity); $this->_preUpdate($entity);
} }
$this->_conn->update($tableName, $updateData[$tableName], $id); $this->_conn->update($tableName, $updateData[$tableName], $id);
if ($this->_evm->hasListeners(Events::postUpdate)) { if ($this->_evm->hasListeners(Events::postUpdate)) {
$this->_postUpdate($entity); $this->_postUpdate($entity);
} }
...@@ -239,9 +241,10 @@ class StandardEntityPersister ...@@ -239,9 +241,10 @@ class StandardEntityPersister
} }
/** /**
* Prepares the data changeset of an entity for database insertion. * Prepares the data changeset of an entity for database insertion (INSERT/UPDATE).
* The array that is passed as the second parameter is filled with *
* <columnName> => <value> pairs, grouped by table name, during this preparation. * During this preparation the array that is passed as the second parameter is filled with
* <columnName> => <value> pairs, grouped by table name.
* *
* Example: * Example:
* <code> * <code>
...@@ -256,7 +259,7 @@ class StandardEntityPersister ...@@ -256,7 +259,7 @@ class StandardEntityPersister
* *
* @param object $entity * @param object $entity
* @param array $result The reference to the data array. * @param array $result The reference to the data array.
* @param boolean $isInsert * @param boolean $isInsert Whether the preparation is for an INSERT (or UPDATE, if FALSE).
*/ */
protected function _prepareData($entity, array &$result, $isInsert = false) protected function _prepareData($entity, array &$result, $isInsert = false)
{ {
...@@ -276,37 +279,43 @@ class StandardEntityPersister ...@@ -276,37 +279,43 @@ class StandardEntityPersister
continue; continue;
} }
// Special case: One-one self-referencing of the same class. // Special case: One-one self-referencing of the same class with IDENTITY type key generation.
if ($newVal !== null && $assocMapping->sourceEntityName == $assocMapping->targetEntityName) { if ($this->_class->isIdGeneratorIdentity() && $newVal !== null &&
$assocMapping->sourceEntityName == $assocMapping->targetEntityName) {
$oid = spl_object_hash($newVal); $oid = spl_object_hash($newVal);
$isScheduledForInsert = $uow->isRegisteredNew($newVal); $isScheduledForInsert = $uow->isRegisteredNew($newVal);
if (isset($this->_queuedInserts[$oid]) || $isScheduledForInsert) { if (isset($this->_queuedInserts[$oid]) || $isScheduledForInsert) {
// The associated entity $newVal is not yet persisted, so we must // The associated entity $newVal is not yet persisted, so we must
// set $newVal = null, in order to insert a null value and update later. // set $newVal = null, in order to insert a null value and schedule an
// extra update on the UnitOfWork.
$uow->scheduleExtraUpdate($entity, array(
$field => array(null, $newVal)
));
$newVal = null; $newVal = null;
} else if ($isInsert && ! $isScheduledForInsert && $uow->getEntityState($newVal) == UnitOfWork::STATE_MANAGED) { } else if ($isInsert && ! $isScheduledForInsert && $uow->getEntityState($newVal) == UnitOfWork::STATE_MANAGED) {
// $newVal is already fully persisted // $newVal is already fully persisted.
// Clear changeset of $newVal, so that only the identifier is updated. // Schedule an extra update for it, so that the foreign key(s) are properly set.
// Not sure this is really rock-solid here but it seems to work. $uow->scheduleExtraUpdate($newVal, array(
$uow->clearEntityChangeSet($oid); $field => array(null, $entity)
$uow->propertyChanged($newVal, $field, $entity, $entity); ));
} }
} }
foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) {
$otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName);
if ($newVal === null) { if ($newVal === null) {
$result[$this->getOwningTable($field)][$sourceColumn] = null; $result[$this->getOwningTable($field)][$sourceColumn] = null;
} else { } else {
$otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName);
$result[$this->getOwningTable($field)][$sourceColumn] = $result[$this->getOwningTable($field)][$sourceColumn] =
$otherClass->reflFields[$otherClass->fieldNames[$targetColumn]]->getValue($newVal); $otherClass->reflFields[$otherClass->fieldNames[$targetColumn]]
->getValue($newVal);
} }
} }
} else if ($newVal === null) { } else if ($newVal === null) {
$result[$this->getOwningTable($field)][$columnName] = null; $result[$this->getOwningTable($field)][$columnName] = null;
} else { } else {
$result[$this->getOwningTable($field)][$columnName] = Type::getType( $result[$this->getOwningTable($field)][$columnName] = Type::getType(
$this->_class->fieldMappings[$field]['type'])->convertToDatabaseValue($newVal, $platform); $this->_class->fieldMappings[$field]['type'])->convertToDatabaseValue($newVal, $platform);
} }
} }
} }
...@@ -404,7 +413,7 @@ class StandardEntityPersister ...@@ -404,7 +413,7 @@ class StandardEntityPersister
return 'SELECT ' . $columnList . ' FROM ' . $this->_class->getTableName() return 'SELECT ' . $columnList . ' FROM ' . $this->_class->getTableName()
. ' WHERE ' . $conditionSql; . ' WHERE ' . $conditionSql;
} }
/** /**
* Dispatches the preInsert event for the given entity. * Dispatches the preInsert event for the given entity.
* *
...@@ -417,7 +426,7 @@ class StandardEntityPersister ...@@ -417,7 +426,7 @@ class StandardEntityPersister
); );
$this->_evm->dispatchEvent(Events::preInsert, $eventArgs); $this->_evm->dispatchEvent(Events::preInsert, $eventArgs);
} }
/** /**
* Dispatches the postInsert event for the given entity. * Dispatches the postInsert event for the given entity.
* *
...@@ -427,7 +436,7 @@ class StandardEntityPersister ...@@ -427,7 +436,7 @@ class StandardEntityPersister
{ {
$this->_evm->dispatchEvent(Events::postInsert); $this->_evm->dispatchEvent(Events::postInsert);
} }
/** /**
* Dispatches the preUpdate event for the given entity. * Dispatches the preUpdate event for the given entity.
* *
...@@ -440,7 +449,7 @@ class StandardEntityPersister ...@@ -440,7 +449,7 @@ class StandardEntityPersister
); );
$this->_evm->dispatchEvent(Events::preUpdate, $eventArgs); $this->_evm->dispatchEvent(Events::preUpdate, $eventArgs);
} }
/** /**
* Dispatches the postUpdate event for the given entity. * Dispatches the postUpdate event for the given entity.
* *
......
...@@ -136,27 +136,29 @@ class UnitOfWork implements PropertyChangedListener ...@@ -136,27 +136,29 @@ class UnitOfWork implements PropertyChangedListener
*/ */
private $_entityUpdates = array(); private $_entityUpdates = array();
private $_extraUpdates = array();
/** /**
* A list of all pending entity deletions. * A list of all pending entity deletions.
* *
* @var array * @var array
*/ */
private $_entityDeletions = array(); private $_entityDeletions = array();
/** /**
* All pending collection deletions. * All pending collection deletions.
* *
* @var array * @var array
*/ */
private $_collectionDeletions = array(); private $_collectionDeletions = array();
/** /**
* All pending collection creations. * All pending collection creations.
* *
* @var array * @var array
*/ */
private $_collectionCreations = array(); private $_collectionCreations = array();
/** /**
* All collection updates. * All collection updates.
* *
...@@ -245,6 +247,10 @@ class UnitOfWork implements PropertyChangedListener ...@@ -245,6 +247,10 @@ class UnitOfWork implements PropertyChangedListener
foreach ($commitOrder as $class) { foreach ($commitOrder as $class) {
$this->_executeUpdates($class); $this->_executeUpdates($class);
} }
// Extra updates that were requested by persisters.
if ($this->_extraUpdates) {
$this->_executeExtraUpdates();
}
// Collection deletions (deletions of complete collections) // Collection deletions (deletions of complete collections)
foreach ($this->_collectionDeletions as $collectionToDelete) { foreach ($this->_collectionDeletions as $collectionToDelete) {
...@@ -278,12 +284,22 @@ class UnitOfWork implements PropertyChangedListener ...@@ -278,12 +284,22 @@ class UnitOfWork implements PropertyChangedListener
$this->_entityInsertions = array(); $this->_entityInsertions = array();
$this->_entityUpdates = array(); $this->_entityUpdates = array();
$this->_entityDeletions = array(); $this->_entityDeletions = array();
$this->_extraUpdates = array();
$this->_entityChangeSets = array(); $this->_entityChangeSets = array();
$this->_collectionUpdates = array(); $this->_collectionUpdates = array();
$this->_collectionDeletions = array(); $this->_collectionDeletions = array();
$this->_visitedCollections = array(); $this->_visitedCollections = array();
} }
protected function _executeExtraUpdates()
{
foreach ($this->_extraUpdates as $oid => $update) {
list ($entity, $changeset) = $update;
$this->_entityChangeSets[$oid] = $changeset;
$this->getEntityPersister(get_class($entity))->update($entity);
}
}
/** /**
* Gets the changeset for an entity. * Gets the changeset for an entity.
* *
...@@ -388,21 +404,19 @@ class UnitOfWork implements PropertyChangedListener ...@@ -388,21 +404,19 @@ class UnitOfWork implements PropertyChangedListener
private function _computeEntityChanges($class, $entity) private function _computeEntityChanges($class, $entity)
{ {
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
if ( ! $class->isInheritanceTypeNone()) { if ( ! $class->isInheritanceTypeNone()) {
$class = $this->_em->getClassMetadata(get_class($entity)); $class = $this->_em->getClassMetadata(get_class($entity));
} }
$actualData = array(); $actualData = array();
foreach ($class->reflFields as $name => $refProp) { foreach ($class->reflFields as $name => $refProp) {
if ( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) { if ( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity()) {
$actualData[$name] = $refProp->getValue($entity); $actualData[$name] = $refProp->getValue($entity);
} }
if ($class->isCollectionValuedAssociation($name) if ($class->isCollectionValuedAssociation($name) && $actualData[$name] !== null
&& $actualData[$name] !== null && ! ($actualData[$name] instanceof PersistentCollection)) {
&& ! ($actualData[$name] instanceof PersistentCollection)
) {
// If $actualData[$name] is Collection then unwrap the array // If $actualData[$name] is Collection then unwrap the array
if ($actualData[$name] instanceof Collection) { if ($actualData[$name] instanceof Collection) {
$actualData[$name] = $actualData[$name]->unwrap(); $actualData[$name] = $actualData[$name]->unwrap();
...@@ -410,7 +424,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -410,7 +424,7 @@ class UnitOfWork implements PropertyChangedListener
$assoc = $class->associationMappings[$name]; $assoc = $class->associationMappings[$name];
// Inject PersistentCollection // Inject PersistentCollection
$coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName), $coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName),
$actualData[$name] ? $actualData[$name] : array()); $actualData[$name] ? $actualData[$name] : array());
$coll->setOwner($entity, $assoc); $coll->setOwner($entity, $assoc);
$coll->setDirty( ! $coll->isEmpty()); $coll->setDirty( ! $coll->isEmpty());
$class->reflFields[$name]->setValue($entity, $coll); $class->reflFields[$name]->setValue($entity, $coll);
...@@ -527,7 +541,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -527,7 +541,7 @@ class UnitOfWork implements PropertyChangedListener
$this->_originalEntityData[$oid] = $data; $this->_originalEntityData[$oid] = $data;
} else if ($state == self::STATE_DELETED) { } else if ($state == self::STATE_DELETED) {
throw DoctrineException::updateMe("Deleted entity in collection detected during flush." throw DoctrineException::updateMe("Deleted entity in collection detected during flush."
. " Make sure you properly remove deleted entities from collections."); . " Make sure you properly remove deleted entities from collections.");
} }
// MANAGED associated entities are already taken into account // MANAGED associated entities are already taken into account
// during changeset calculation anyway, since they are in the identity map. // during changeset calculation anyway, since they are in the identity map.
...@@ -609,12 +623,13 @@ class UnitOfWork implements PropertyChangedListener ...@@ -609,12 +623,13 @@ class UnitOfWork implements PropertyChangedListener
$entityChangeSet = array_merge( $entityChangeSet = array_merge(
$this->_entityInsertions, $this->_entityInsertions,
$this->_entityUpdates, $this->_entityUpdates,
$this->_entityDeletions); $this->_entityDeletions
);
} }
// TODO: We can cache computed commit orders in the metadata cache! // TODO: We can cache computed commit orders in the metadata cache!
// Check cache at this point here! // Check cache at this point here!
// See if there are any new classes in the changeset, that are not in the // See if there are any new classes in the changeset, that are not in the
// commit order graph yet (dont have a node). // commit order graph yet (dont have a node).
$newNodes = array(); $newNodes = array();
...@@ -622,9 +637,9 @@ class UnitOfWork implements PropertyChangedListener ...@@ -622,9 +637,9 @@ class UnitOfWork implements PropertyChangedListener
$className = get_class($entity); $className = get_class($entity);
if ( ! $this->_commitOrderCalculator->hasNodeWithKey($className)) { if ( ! $this->_commitOrderCalculator->hasNodeWithKey($className)) {
$this->_commitOrderCalculator->addNodeWithItem( $this->_commitOrderCalculator->addNodeWithItem(
$className, // index/key $className, // index/key
$this->_em->getClassMetadata($className) // item $this->_em->getClassMetadata($className) // item
); );
$newNodes[] = $this->_commitOrderCalculator->getNodeForKey($className); $newNodes[] = $this->_commitOrderCalculator->getNodeForKey($className);
} }
} }
...@@ -640,9 +655,9 @@ class UnitOfWork implements PropertyChangedListener ...@@ -640,9 +655,9 @@ class UnitOfWork implements PropertyChangedListener
// If the target class does not yet have a node, create it // If the target class does not yet have a node, create it
if ( ! $this->_commitOrderCalculator->hasNodeWithKey($targetClassName)) { if ( ! $this->_commitOrderCalculator->hasNodeWithKey($targetClassName)) {
$this->_commitOrderCalculator->addNodeWithItem( $this->_commitOrderCalculator->addNodeWithItem(
$targetClassName, // index/key $targetClassName, // index/key
$targetClass // item $targetClass // item
); );
} }
// add dependency // add dependency
$otherNode = $this->_commitOrderCalculator->getNodeForKey($targetClassName); $otherNode = $this->_commitOrderCalculator->getNodeForKey($targetClassName);
...@@ -657,7 +672,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -657,7 +672,7 @@ class UnitOfWork implements PropertyChangedListener
/** /**
* Registers a new entity. The entity will be scheduled for insertion. * Registers a new entity. The entity will be scheduled for insertion.
* If the entity already has an identifier, it will be added to the identity map. * If the entity already has an identifier, it will be added to the identity map.
* *
* @param object $entity * @param object $entity
* @todo Rename to scheduleForInsert(). * @todo Rename to scheduleForInsert().
*/ */
...@@ -715,6 +730,11 @@ class UnitOfWork implements PropertyChangedListener ...@@ -715,6 +730,11 @@ class UnitOfWork implements PropertyChangedListener
} }
} }
public function scheduleExtraUpdate($entity, array $changeset)
{
$this->_extraUpdates[spl_object_hash($entity)] = array($entity, $changeset);
}
/** /**
* Checks whether an entity is registered as dirty in the unit of work. * Checks whether an entity is registered as dirty in the unit of work.
* Note: Is not very useful currently as dirty entities are only registered * Note: Is not very useful currently as dirty entities are only registered
...@@ -731,7 +751,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -731,7 +751,7 @@ class UnitOfWork implements PropertyChangedListener
/** /**
* Registers a deleted entity. * Registers a deleted entity.
* *
* @todo Rename to scheduleForDelete(). * @todo Rename to scheduleForDelete().
*/ */
public function registerDeleted($entity) public function registerDeleted($entity)
...@@ -781,8 +801,8 @@ class UnitOfWork implements PropertyChangedListener ...@@ -781,8 +801,8 @@ class UnitOfWork implements PropertyChangedListener
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
$this->removeFromIdentityMap($entity); $this->removeFromIdentityMap($entity);
unset($this->_entityInsertions[$oid], $this->_entityUpdates[$oid], unset($this->_entityInsertions[$oid], $this->_entityUpdates[$oid],
$this->_entityDeletions[$oid], $this->_entityIdentifiers[$oid], $this->_entityDeletions[$oid], $this->_entityIdentifiers[$oid],
$this->_entityStates[$oid]); $this->_entityStates[$oid]);
} }
public function isEntityRegistered($entity) public function isEntityRegistered($entity)
...@@ -832,12 +852,12 @@ class UnitOfWork implements PropertyChangedListener ...@@ -832,12 +852,12 @@ class UnitOfWork implements PropertyChangedListener
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
if ( ! isset($this->_entityStates[$oid])) { if ( ! isset($this->_entityStates[$oid])) {
/*if (isset($this->_entityInsertions[$oid])) { /*if (isset($this->_entityInsertions[$oid])) {
$this->_entityStates[$oid] = self::STATE_NEW; $this->_entityStates[$oid] = self::STATE_NEW;
} else if ( ! isset($this->_entityIdentifiers[$oid])) { } else if ( ! isset($this->_entityIdentifiers[$oid])) {
// Either NEW (if no ID) or DETACHED (if ID) // Either NEW (if no ID) or DETACHED (if ID)
} else { } else {
$this->_entityStates[$oid] = self::STATE_DETACHED; $this->_entityStates[$oid] = self::STATE_DETACHED;
}*/ }*/
if (isset($this->_entityIdentifiers[$oid]) && ! isset($this->_entityInsertions[$oid])) { if (isset($this->_entityIdentifiers[$oid]) && ! isset($this->_entityInsertions[$oid])) {
$this->_entityStates[$oid] = self::STATE_DETACHED; $this->_entityStates[$oid] = self::STATE_DETACHED;
} else { } else {
...@@ -918,10 +938,8 @@ class UnitOfWork implements PropertyChangedListener ...@@ -918,10 +938,8 @@ class UnitOfWork implements PropertyChangedListener
if ($idHash === '') { if ($idHash === '') {
return false; return false;
} }
return isset($this->_identityMap return isset($this->_identityMap[$classMetadata->rootEntityName][$idHash]);
[$classMetadata->rootEntityName]
[$idHash]);
} }
/** /**
...@@ -954,6 +972,11 @@ class UnitOfWork implements PropertyChangedListener ...@@ -954,6 +972,11 @@ class UnitOfWork implements PropertyChangedListener
foreach ($commitOrder as $class) { foreach ($commitOrder as $class) {
$this->_executeInserts($class); $this->_executeInserts($class);
} }
// Extra updates that were requested by persisters.
if ($this->_extraUpdates) {
$this->_executeExtraUpdates();
$this->_extraUpdates = array();
}
// remove them from _entityInsertions and _entityChangeSets // remove them from _entityInsertions and _entityChangeSets
$this->_entityInsertions = array_diff_key($this->_entityInsertions, $insertNow); $this->_entityInsertions = array_diff_key($this->_entityInsertions, $insertNow);
$this->_entityChangeSets = array_diff_key($this->_entityChangeSets, $insertNow); $this->_entityChangeSets = array_diff_key($this->_entityChangeSets, $insertNow);
...@@ -1035,7 +1058,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1035,7 +1058,7 @@ class UnitOfWork implements PropertyChangedListener
/** /**
* Deletes an entity as part of the current unit of work. * Deletes an entity as part of the current unit of work.
* *
* This method is internally called during delete() cascades as it tracks * This method is internally called during delete() cascades as it tracks
* the already visited entities to prevent infinite recursions. * the already visited entities to prevent infinite recursions.
* *
...@@ -1092,7 +1115,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1092,7 +1115,7 @@ class UnitOfWork implements PropertyChangedListener
$id = $class->getIdentifierValues($entity); $id = $class->getIdentifierValues($entity);
if ( ! $id) { if ( ! $id) {
throw new InvalidArgumentException('New entity passed to merge().'); throw new \InvalidArgumentException('New entity passed to merge().');
} }
$managedCopy = $this->tryGetById($id, $class->rootEntityName); $managedCopy = $this->tryGetById($id, $class->rootEntityName);
...@@ -1134,7 +1157,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1134,7 +1157,7 @@ class UnitOfWork implements PropertyChangedListener
/** /**
* Cascades a merge operation to associated entities. * Cascades a merge operation to associated entities.
* *
* @param object $entity * @param object $entity
* @param object $managedCopy * @param object $managedCopy
* @param array $visited * @param array $visited
...@@ -1237,34 +1260,34 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1237,34 +1260,34 @@ class UnitOfWork implements PropertyChangedListener
$this->_collectionUpdates = array(); $this->_collectionUpdates = array();
$this->_commitOrderCalculator->clear(); $this->_commitOrderCalculator->clear();
} }
public function scheduleCollectionUpdate(PersistentCollection $coll) public function scheduleCollectionUpdate(PersistentCollection $coll)
{ {
$this->_collectionUpdates[] = $coll; $this->_collectionUpdates[] = $coll;
} }
public function isCollectionScheduledForUpdate(PersistentCollection $coll) public function isCollectionScheduledForUpdate(PersistentCollection $coll)
{ {
//... //...
} }
public function scheduleCollectionDeletion(PersistentCollection $coll) public function scheduleCollectionDeletion(PersistentCollection $coll)
{ {
//TODO: if $coll is already scheduled for recreation ... what to do? //TODO: if $coll is already scheduled for recreation ... what to do?
// Just remove $coll from the scheduled recreations? // Just remove $coll from the scheduled recreations?
$this->_collectionDeletions[] = $coll; $this->_collectionDeletions[] = $coll;
} }
public function isCollectionScheduledForDeletion(PersistentCollection $coll) public function isCollectionScheduledForDeletion(PersistentCollection $coll)
{ {
//... //...
} }
public function scheduleCollectionRecreation(PersistentCollection $coll) public function scheduleCollectionRecreation(PersistentCollection $coll)
{ {
$this->_collectionRecreations[] = $coll; $this->_collectionRecreations[] = $coll;
} }
public function isCollectionScheduledForRecreation(PersistentCollection $coll) public function isCollectionScheduledForRecreation(PersistentCollection $coll)
{ {
//... //...
...@@ -1498,7 +1521,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1498,7 +1521,7 @@ class UnitOfWork implements PropertyChangedListener
$this->_originalEntityData[$oid] = $data; $this->_originalEntityData[$oid] = $data;
$this->addToIdentityMap($entity); $this->addToIdentityMap($entity);
} }
/** /**
* INTERNAL: * INTERNAL:
* Clears the property changeset of the entity with the given OID. * Clears the property changeset of the entity with the given OID.
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
namespace Doctrine\Tests\Models\ECommerce; namespace Doctrine\Tests\Models\ECommerce;
use Doctrine\Common\Collections\Collection;
/** /**
* ECommerceCart * ECommerceCart
* Represents a typical cart of a shopping application. * Represents a typical cart of a shopping application.
...@@ -40,7 +42,7 @@ class ECommerceCart ...@@ -40,7 +42,7 @@ class ECommerceCart
public function __construct() public function __construct()
{ {
$this->products = new \Doctrine\Common\Collections\Collection; $this->products = new Collection;
} }
public function getId() { public function getId() {
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
namespace Doctrine\Tests\Models\ECommerce; namespace Doctrine\Tests\Models\ECommerce;
use Doctrine\Common\Collections\Collection;
/** /**
* ECommerceCategory * ECommerceCategory
* Represents a tag applied on particular products. * Represents a tag applied on particular products.
...@@ -42,8 +44,8 @@ class ECommerceCategory ...@@ -42,8 +44,8 @@ class ECommerceCategory
public function __construct() public function __construct()
{ {
$this->products = new \Doctrine\Common\Collections\Collection(); $this->products = new Collection();
$this->children = new \Doctrine\Common\Collections\Collection(); $this->children = new Collection();
} }
public function getId() public function getId()
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
namespace Doctrine\Tests\Models\ECommerce; namespace Doctrine\Tests\Models\ECommerce;
use Doctrine\Common\Collections\Collection;
/** /**
* ECommerceProduct * ECommerceProduct
* Represents a type of product of a shopping application. * Represents a type of product of a shopping application.
...@@ -45,8 +47,8 @@ class ECommerceProduct ...@@ -45,8 +47,8 @@ class ECommerceProduct
public function __construct() public function __construct()
{ {
$this->features = new \Doctrine\Common\Collections\Collection; $this->features = new Collection;
$this->categories = new \Doctrine\Common\Collections\Collection; $this->categories = new Collection;
} }
public function getId() public function getId()
......
...@@ -13,8 +13,7 @@ namespace Doctrine\Tests\Models\ECommerce; ...@@ -13,8 +13,7 @@ namespace Doctrine\Tests\Models\ECommerce;
class ECommerceShipping class ECommerceShipping
{ {
/** /**
* @Column(type="integer") * @Id @Column(type="integer")
* @Id
* @GeneratedValue(strategy="AUTO") * @GeneratedValue(strategy="AUTO")
*/ */
private $id; private $id;
......
...@@ -38,7 +38,9 @@ class AbstractManyToManyAssociationTestCase extends \Doctrine\Tests\OrmFunctiona ...@@ -38,7 +38,9 @@ class AbstractManyToManyAssociationTestCase extends \Doctrine\Tests\OrmFunctiona
public function assertCollectionEquals(Collection $first, Collection $second) public function assertCollectionEquals(Collection $first, Collection $second)
{ {
if (count($first) != count($second)) { return $first->forAll(function($k, $e) use($second) { return $second->contains($e); });
/*if (count($first) != count($second)) {
return false; return false;
} }
foreach ($first as $element) { foreach ($first as $element) {
...@@ -46,6 +48,6 @@ class AbstractManyToManyAssociationTestCase extends \Doctrine\Tests\OrmFunctiona ...@@ -46,6 +48,6 @@ class AbstractManyToManyAssociationTestCase extends \Doctrine\Tests\OrmFunctiona
return false; return false;
} }
} }
return true; return true;*/
} }
} }
...@@ -31,6 +31,7 @@ class AllTests ...@@ -31,6 +31,7 @@ class AllTests
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\OneToManyBidirectionalAssociationTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\OneToManyBidirectionalAssociationTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ManyToManyUnidirectionalAssociationTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\ManyToManyUnidirectionalAssociationTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ManyToManyBidirectionalAssociationTest'); $suite->addTestSuite('Doctrine\Tests\ORM\Functional\ManyToManyBidirectionalAssociationTest');
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\OneToManySelfReferentialAssociationTest');
return $suite; return $suite;
} }
......
...@@ -32,6 +32,8 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio ...@@ -32,6 +32,8 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio
$this->parent->addChild($this->secondChild); $this->parent->addChild($this->secondChild);
$this->_em->save($this->parent); $this->_em->save($this->parent);
$this->_em->flush();
$this->assertForeignKeyIs($this->parent->getId(), $this->firstChild); $this->assertForeignKeyIs($this->parent->getId(), $this->firstChild);
$this->assertForeignKeyIs($this->parent->getId(), $this->secondChild); $this->assertForeignKeyIs($this->parent->getId(), $this->secondChild);
} }
...@@ -39,6 +41,7 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio ...@@ -39,6 +41,7 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio
public function testSavesAnEmptyCollection() public function testSavesAnEmptyCollection()
{ {
$this->_em->save($this->parent); $this->_em->save($this->parent);
$this->_em->flush();
$this->assertEquals(0, count($this->parent->getChildren())); $this->assertEquals(0, count($this->parent->getChildren()));
} }
...@@ -46,6 +49,7 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio ...@@ -46,6 +49,7 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio
public function testDoesNotSaveAnInverseSideSet() { public function testDoesNotSaveAnInverseSideSet() {
$this->parent->brokenAddChild($this->firstChild); $this->parent->brokenAddChild($this->firstChild);
$this->_em->save($this->parent); $this->_em->save($this->parent);
$this->_em->flush();
$this->assertForeignKeyIs(null, $this->firstChild); $this->assertForeignKeyIs(null, $this->firstChild);
} }
...@@ -80,10 +84,10 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio ...@@ -80,10 +84,10 @@ class OneToManySelfReferentialAssociationTest extends \Doctrine\Tests\OrmFunctio
$this->assertTrue($children[0] instanceof ECommerceCategory); $this->assertTrue($children[0] instanceof ECommerceCategory);
$this->assertSame($parent, $children[0]->getParent()); $this->assertSame($parent, $children[0]->getParent());
$this->assertTrue(strstr($children[0]->getName(), ' books')); $this->assertEquals(' books', strstr($children[0]->getName(), ' books'));
$this->assertTrue($children[1] instanceof ECommerceCategory); $this->assertTrue($children[1] instanceof ECommerceCategory);
$this->assertSame($parent, $children[1]->getParent()); $this->assertSame($parent, $children[1]->getParent());
$this->assertTrue(strstr($children[1]->getName(), ' books')); $this->assertEquals(' books', strstr($children[1]->getName(), ' books'));
} }
/* TODO: not yet implemented /* TODO: not yet implemented
......
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