diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 9be5b50da..04a30dc46 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -413,6 +413,7 @@ class EntityManager implements ObjectManager $entity = $class->newInstance(); $class->setIdentifierValues($entity, $identifier); $this->unitOfWork->registerManaged($entity, $identifier, array()); + $this->unitOfWork->markReadOnly($entity); return $entity; } diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index ea516f3d0..06541cee0 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -215,8 +215,13 @@ class UnitOfWork implements PropertyChangedListener * @var array */ private $orphanRemovals = array(); - - //private $_readOnlyObjects = array(); + + /** + * Read-Only objects are never evaluated + * + * @var array + */ + private $readOnlyObjects = array(); /** * Map of Entity Class-Names and corresponding IDs that should eager loaded when requested. @@ -403,6 +408,11 @@ class UnitOfWork implements PropertyChangedListener } $oid = spl_object_hash($entity); + + if (isset($this->readOnlyObjects[$oid])) { + return; + } + $actualData = array(); foreach ($class->reflFields as $name => $refProp) { $value = $refProp->getValue($entity); @@ -459,6 +469,15 @@ class UnitOfWork implements PropertyChangedListener foreach ($actualData as $propName => $actualValue) { $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; + if (isset($originalData[$propName])) { + $orgValue = $originalData[$propName]; + } else if (array_key_exists($propName, $originalData)) { + $orgValue = null; + } else { + // skip field, its a partially omitted one! + continue; + } + if (isset($class->associationMappings[$propName])) { $assoc = $class->associationMappings[$propName]; if ($assoc['type'] & ClassMetadata::TO_ONE && $orgValue !== $actualValue) { @@ -528,7 +547,7 @@ class UnitOfWork implements PropertyChangedListener foreach ($entitiesToProcess as $entity) { // Ignore uninitialized proxy objects - if (/* $entity is readOnly || */ $entity instanceof Proxy && ! $entity->__isInitialized__) { + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { continue; } // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here. @@ -2407,4 +2426,37 @@ class UnitOfWork implements PropertyChangedListener { return method_exists($obj, '__toString') ? (string)$obj : get_class($obj).'@'.spl_object_hash($obj); } + + /** + * Marks an entity as read-only so that it will not be considered for updates during UnitOfWork#commit(). + * + * This operation cannot be undone as some parts of the UnitOfWork now keep gathering information + * on this object that might be necessary to perform a correct udpate. + * + * @throws \InvalidArgumentException + * @param $object + * @return void + */ + public function markReadOnly($object) + { + if ( ! is_object($object) || ! $this->isInIdentityMap($object)) { + throw new InvalidArgumentException("Managed entity required"); + } + $this->readOnlyObjects[spl_object_hash($object)] = true; + } + + /** + * Is this entity read only? + * + * @throws \InvalidArgumentException + * @param $object + * @return void + */ + public function isReadOnly($object) + { + if ( ! is_object($object) ) { + throw new InvalidArgumentException("Managed entity required"); + } + return $this->readOnlyObjects[spl_object_hash($object)]; + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index bed8de33d..8d636dffb 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -863,7 +863,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testGetPartialReferenceToUpdateObjectWithoutLoadingIt() { - //$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); $user = new CmsUser(); $user->username = "beberlei"; $user->name = "Benjamin E."; @@ -882,7 +881,7 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->flush(); $this->_em->clear(); - $this->assertEquals('Stephan', $this->_em->find(get_class($user), $userId)->name); + $this->assertEquals('Benjamin E.', $this->_em->find(get_class($user), $userId)->name); } public function testMergePersistsNewEntities() diff --git a/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php b/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php index 3da6bc09b..2e17ccf12 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php @@ -54,7 +54,30 @@ class DefaultValuesTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals($userId, $a2->getUser()->getId()); $this->assertEquals('Poweruser', $a2->getUser()->type); } - + + /** + * @group DDC-1386 + */ + public function testGetPartialReferenceWithDefaultValueNotEvalutedInFlush() + { + $user = new DefaultValueUser; + $user->name = 'romanb'; + $user->type = 'Normaluser'; + + $this->_em->persist($user); + $this->_em->flush(); + $this->_em->clear(); + + $user = $this->_em->getPartialReference('Doctrine\Tests\ORM\Functional\DefaultValueUser', $user->id); + $this->assertTrue($this->_em->getUnitOfWork()->isReadOnly($user)); + + $this->_em->flush(); + $this->_em->clear(); + + $user = $this->_em->find('Doctrine\Tests\ORM\Functional\DefaultValueUser', $user->id); + + $this->assertEquals('Normaluser', $user->type); + } }