Commit d2405ded authored by romanb's avatar romanb

[2.0] Enhanced one-to-one self-referential association handling.

parent 3135799f
...@@ -288,11 +288,14 @@ class ObjectHydrator extends AbstractHydrator ...@@ -288,11 +288,14 @@ class ObjectHydrator extends AbstractHydrator
if ($relation->isOwningSide) { if ($relation->isOwningSide) {
// If there is an inverse mapping on the target class its bidirectional // If there is an inverse mapping on the target class its bidirectional
if (isset($targetClass->inverseMappings[$property])) { if (isset($targetClass->inverseMappings[$property])) {
$sourceProp = $targetClass->inverseMappings[$fieldName]->sourceFieldName; $sourceProp = $targetClass->inverseMappings[$property]->sourceFieldName;
$targetClass->reflFields[$sourceProp]->setValue($entity2, $entity1); $targetClass->reflFields[$sourceProp]->setValue($entity2, $entity1);
} else if ($classMetadata1 === $targetClass) {
// Special case: self-referencing one-one on the same class
$targetClass->reflFields[$property]->setValue($entity2, $entity1);
} }
} else { } else {
// For sure bidirectional, as there is no inverse side in unidirectional // For sure bidirectional, as there is no inverse side in unidirectional mappings
$targetClass->reflFields[$relation->mappedByFieldName]->setValue($entity2, $entity1); $targetClass->reflFields[$relation->mappedByFieldName]->setValue($entity2, $entity1);
} }
} }
......
...@@ -23,6 +23,7 @@ namespace Doctrine\ORM\Persisters; ...@@ -23,6 +23,7 @@ namespace Doctrine\ORM\Persisters;
use Doctrine\DBAL\Types\Type; use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadata;
...@@ -92,7 +93,7 @@ class StandardEntityPersister ...@@ -92,7 +93,7 @@ class StandardEntityPersister
*/ */
public function addInsert($entity) public function addInsert($entity)
{ {
$this->_queuedInserts[] = $entity; $this->_queuedInserts[spl_object_hash($entity)] = $entity;
} }
/** /**
...@@ -237,16 +238,22 @@ class StandardEntityPersister ...@@ -237,16 +238,22 @@ class StandardEntityPersister
continue; continue;
} }
//TODO: If the one-one is self-referencing, check whether the referenced entity ($newVal) // Special case: One-one self-referencing of the same class.
// is still scheduled for insertion. If so: if ($newVal !== null && $assocMapping->sourceEntityName == $assocMapping->targetEntityName) {
// 1) set $newVal = null, so that we insert a null value $oid = spl_object_hash($newVal);
// 2) schedule $entity for an update, so that the FK gets set through an update $isScheduledForInsert = $uow->isRegisteredNew($newVal);
// later, after the referenced entity has been inserted. if (isset($this->_queuedInserts[$oid]) || $isScheduledForInsert) {
//$needsPostponedUpdate = ... // The associated entity $newVal is not yet persisted, so we must
/*if ($assocMapping->sourceEntityName == $assocMapping->targetEntityName && // set $newVal = null, in order to insert a null value and update later.
isset($this->_queuedInserts[spl_object_hash($entity)])) { $newVal = null;
echo "SELF-REFERENCING!"; } else if ($isInsert && ! $isScheduledForInsert && $uow->getEntityState($newVal) == UnitOfWork::STATE_MANAGED) {
}*/ // $newVal is already fully persisted
// Clear changeset of $newVal, so that only the identifier is updated.
// Not sure this is really rock-solid here but it seems to work.
$uow->clearEntityChangeSet($oid);
$uow->propertyChanged($newVal, $field, $entity, $entity);
}
}
foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) {
$otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName); $otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName);
......
...@@ -269,8 +269,6 @@ class UnitOfWork implements PropertyChangedListener ...@@ -269,8 +269,6 @@ class UnitOfWork implements PropertyChangedListener
throw $e; throw $e;
} }
//TODO: commit transaction here?
// Take new snapshots from visited collections // Take new snapshots from visited collections
foreach ($this->_visitedCollections as $coll) { foreach ($this->_visitedCollections as $coll) {
$coll->takeSnapshot(); $coll->takeSnapshot();
...@@ -548,9 +546,10 @@ class UnitOfWork implements PropertyChangedListener ...@@ -548,9 +546,10 @@ class UnitOfWork implements PropertyChangedListener
{ {
$className = $class->name; $className = $class->name;
$persister = $this->getEntityPersister($className); $persister = $this->getEntityPersister($className);
foreach ($this->_entityInsertions as $entity) { foreach ($this->_entityInsertions as $oid => $entity) {
if (get_class($entity) == $className) { if (get_class($entity) == $className) {
$persister->addInsert($entity); $persister->addInsert($entity);
unset($this->_entityInsertions[$oid]);
} }
} }
$postInsertIds = $persister->executeInserts(); $postInsertIds = $persister->executeInserts();
...@@ -577,9 +576,10 @@ class UnitOfWork implements PropertyChangedListener ...@@ -577,9 +576,10 @@ class UnitOfWork implements PropertyChangedListener
{ {
$className = $class->name; $className = $class->name;
$persister = $this->getEntityPersister($className); $persister = $this->getEntityPersister($className);
foreach ($this->_entityUpdates as $entity) { foreach ($this->_entityUpdates as $oid => $entity) {
if (get_class($entity) == $className) { if (get_class($entity) == $className) {
$persister->update($entity); $persister->update($entity);
unset($this->_entityUpdates[$oid]);
} }
} }
} }
...@@ -593,9 +593,10 @@ class UnitOfWork implements PropertyChangedListener ...@@ -593,9 +593,10 @@ class UnitOfWork implements PropertyChangedListener
{ {
$className = $class->name; $className = $class->name;
$persister = $this->getEntityPersister($className); $persister = $this->getEntityPersister($className);
foreach ($this->_entityDeletions as $entity) { foreach ($this->_entityDeletions as $oid => $entity) {
if (get_class($entity) == $className) { if (get_class($entity) == $className) {
$persister->delete($entity); $persister->delete($entity);
unset($this->_entityDeletions[$oid]);
} }
} }
} }
...@@ -1533,6 +1534,17 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1533,6 +1534,17 @@ class UnitOfWork implements PropertyChangedListener
$this->addToIdentityMap($entity); $this->addToIdentityMap($entity);
} }
/**
* INTERNAL:
* Clears the property changeset of the entity with the given OID.
*
* @param string $oid The entity's OID.
*/
public function clearEntityChangeSet($oid)
{
unset($this->_entityChangeSets[$oid]);
}
/* PropertyChangedListener implementation */ /* PropertyChangedListener implementation */
/** /**
...@@ -1552,7 +1564,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1552,7 +1564,7 @@ class UnitOfWork implements PropertyChangedListener
$this->_entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue); $this->_entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue);
if (isset($class->associationMappings[$propertyName])) { if (isset($class->associationMappings[$propertyName])) {
$assoc = $class->associationMappings[$name]; $assoc = $class->associationMappings[$propertyName];
if ($assoc->isOneToOne() && $assoc->isOwningSide) { if ($assoc->isOneToOne() && $assoc->isOwningSide) {
$this->_entityUpdates[$oid] = $entity; $this->_entityUpdates[$oid] = $entity;
} else if ($oldValue instanceof PersistentCollection) { } else if ($oldValue instanceof PersistentCollection) {
......
...@@ -146,8 +146,7 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase ...@@ -146,8 +146,7 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertTrue($result[0] instanceof CompanyPerson); $this->assertTrue($result[0] instanceof CompanyPerson);
$this->assertEquals('Mary Smith', $result[0]->getName()); $this->assertEquals('Mary Smith', $result[0]->getName());
$this->assertTrue($result[0]->getSpouse() instanceof CompanyEmployee); $this->assertTrue($result[0]->getSpouse() instanceof CompanyEmployee);
$this->assertEquals('John Smith', $result[0]->getSpouse()->getName());
//var_dump($result); $this->assertSame($result[0], $result[0]->getSpouse()->getSpouse());
} }
} }
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