diff --git a/lib/Doctrine/ODM/PHPCR/UnitOfWork.php b/lib/Doctrine/ODM/PHPCR/UnitOfWork.php index 2c883e52c..2cb50e688 100644 --- a/lib/Doctrine/ODM/PHPCR/UnitOfWork.php +++ b/lib/Doctrine/ODM/PHPCR/UnitOfWork.php @@ -1441,7 +1441,7 @@ private function computeChildrenChanges($document, $class, $oid, $isNew, $change * @param ClassMetadata $class * @param object $document */ - public function computeChangeSet(ClassMetadata $class, $document) + public function computeChangeSet(ClassMetadata $class, $document, $fieldsOnly = false) { if ($document instanceof Proxy && !$document->__isInitialized()) { return; @@ -1467,7 +1467,7 @@ public function computeChangeSet(ClassMetadata $class, $document) } } - if ($class->parentMapping && isset($changeSet[$class->parentMapping])) { + if (!$fieldsOnly && $class->parentMapping && isset($changeSet[$class->parentMapping])) { $parent = $changeSet[$class->parentMapping]; $parentClass = $this->dm->getClassMetadata(get_class($parent)); $state = $this->getDocumentState($parent); @@ -1477,90 +1477,92 @@ public function computeChangeSet(ClassMetadata $class, $document) } } - foreach ($class->childMappings as $fieldName) { - if ($changeSet[$fieldName]) { - if (is_array($changeSet[$fieldName]) || $changeSet[$fieldName] instanceof Collection) { - throw PHPCRException::childFieldIsArray( - self::objToStr($document, $this->dm), - $fieldName - ); - } + if (!$fieldsOnly) { + foreach ($class->childMappings as $fieldName) { + if ($changeSet[$fieldName]) { + if (is_array($changeSet[$fieldName]) || $changeSet[$fieldName] instanceof Collection) { + throw PHPCRException::childFieldIsArray( + self::objToStr($document, $this->dm), + $fieldName + ); + } - if (!is_object($changeSet[$fieldName])) { - throw PHPCRException::childFieldNoObject( - self::objToStr($document, $this->dm), - $fieldName, - gettype($changeSet[$fieldName]) - ); - } + if (!is_object($changeSet[$fieldName])) { + throw PHPCRException::childFieldNoObject( + self::objToStr($document, $this->dm), + $fieldName, + gettype($changeSet[$fieldName]) + ); + } - $mapping = $class->mappings[$fieldName]; - $changeSet[$fieldName] = $this->computeChildChanges($mapping, $changeSet[$fieldName], $id, $mapping['nodeName']); + $mapping = $class->mappings[$fieldName]; + $changeSet[$fieldName] = $this->computeChildChanges($mapping, $changeSet[$fieldName], $id, $mapping['nodeName']); + } } - } - $this->computeAssociationChanges($document, $class, $oid, $isNew, $changeSet, 'reference'); - $this->computeAssociationChanges($document, $class, $oid, $isNew, $changeSet, 'referrer'); + $this->computeAssociationChanges($document, $class, $oid, $isNew, $changeSet, 'reference'); + $this->computeAssociationChanges($document, $class, $oid, $isNew, $changeSet, 'referrer'); - foreach ($class->mixedReferrersMappings as $fieldName) { - if ($changeSet[$fieldName] - && $changeSet[$fieldName] instanceof PersistentCollection - && $changeSet[$fieldName]->isDirty() - ) { - throw new PHPCRException("The immutable mixed referrer collection in field $fieldName is dirty"); + foreach ($class->mixedReferrersMappings as $fieldName) { + if ($changeSet[$fieldName] + && $changeSet[$fieldName] instanceof PersistentCollection + && $changeSet[$fieldName]->isDirty() + ) { + throw new PHPCRException("The immutable mixed referrer collection in field $fieldName is dirty"); + } } - } - - $this->computeChildrenChanges($document, $class, $oid, $isNew, $changeSet); - - if (!$isNew) { - // collect assignment move operations - $destPath = $destName = false; - if (isset($this->originalData[$oid][$class->parentMapping]) - && isset($changeSet[$class->parentMapping]) - && $this->originalData[$oid][$class->parentMapping] !== $changeSet[$class->parentMapping] - ) { - $destPath = $this->getDocumentId($changeSet[$class->parentMapping]); - } + $this->computeChildrenChanges($document, $class, $oid, $isNew, $changeSet); - if (isset($this->originalData[$oid][$class->nodename]) - && isset($changeSet[$class->nodename]) - && $this->originalData[$oid][$class->nodename] !== $changeSet[$class->nodename] - ) { - $destName = $changeSet[$class->nodename]; - } + if (!$isNew) { + // collect assignment move operations + $destPath = $destName = false; - // there was assignment move - if ($destPath || $destName) { - // add the other field if only one was changed - if (false === $destPath) { - $destPath = isset($changeSet[$class->parentMapping]) - ? $this->getDocumentId($changeSet[$class->parentMapping]) - : PathHelper::getParentPath($this->getDocumentId($document)); - } - if (false === $destName) { - $destName = $class->nodename !== null && $changeSet[$class->nodename] - ? $changeSet[$class->nodename] - : PathHelper::getNodeName($this->getDocumentId($document)); + if (isset($this->originalData[$oid][$class->parentMapping]) + && isset($changeSet[$class->parentMapping]) + && $this->originalData[$oid][$class->parentMapping] !== $changeSet[$class->parentMapping] + ) { + $destPath = $this->getDocumentId($changeSet[$class->parentMapping]); } - // make sure destination nodename is okay - if ($exception = $class->isValidNodename($destName)) { - throw IdException::illegalName($document, $class->nodename, $destName); + if (isset($this->originalData[$oid][$class->nodename]) + && isset($changeSet[$class->nodename]) + && $this->originalData[$oid][$class->nodename] !== $changeSet[$class->nodename] + ) { + $destName = $changeSet[$class->nodename]; } - // prevent path from becoming "//foobar" when moving to root node. - $targetPath = ('/' == $destPath) ? "/$destName" : "$destPath/$destName"; + // there was assignment move + if ($destPath || $destName) { + // add the other field if only one was changed + if (false === $destPath) { + $destPath = isset($changeSet[$class->parentMapping]) + ? $this->getDocumentId($changeSet[$class->parentMapping]) + : PathHelper::getParentPath($this->getDocumentId($document)); + } + if (false === $destName) { + $destName = $class->nodename !== null && $changeSet[$class->nodename] + ? $changeSet[$class->nodename] + : PathHelper::getNodeName($this->getDocumentId($document)); + } - $this->scheduleMove($document, $targetPath); - } + // make sure destination nodename is okay + if ($exception = $class->isValidNodename($destName)) { + throw IdException::illegalName($document, $class->nodename, $destName); + } - if (isset($this->originalData[$oid][$class->identifier]) - && isset($changeSet[$class->identifier]) - && $this->originalData[$oid][$class->identifier] !== $changeSet[$class->identifier] - ) { - throw new PHPCRException('The Id is immutable ('.$this->originalData[$oid][$class->identifier].' !== '.$changeSet[$class->identifier].'). Please use DocumentManager::move to move the document: '.self::objToStr($document, $this->dm)); + // prevent path from becoming "//foobar" when moving to root node. + $targetPath = ('/' == $destPath) ? "/$destName" : "$destPath/$destName"; + + $this->scheduleMove($document, $targetPath); + } + + if (isset($this->originalData[$oid][$class->identifier]) + && isset($changeSet[$class->identifier]) + && $this->originalData[$oid][$class->identifier] !== $changeSet[$class->identifier] + ) { + throw new PHPCRException('The Id is immutable ('.$this->originalData[$oid][$class->identifier].' !== '.$changeSet[$class->identifier].'). Please use DocumentManager::move to move the document: '.self::objToStr($document, $this->dm)); + } } } @@ -2479,7 +2481,7 @@ private function executeUpdates($documents, $dispatchEvents = true) $invoke ); $this->changesetComputed = array_diff($this->changesetComputed, array($oid)); - $this->computeChangeSet($class, $document); + $this->computeChangeSet($class, $document, true); } } diff --git a/tests/Doctrine/Tests/ODM/PHPCR/Functional/RenameChildTest.php b/tests/Doctrine/Tests/ODM/PHPCR/Functional/RenameChildTest.php new file mode 100644 index 000000000..f7a80f599 --- /dev/null +++ b/tests/Doctrine/Tests/ODM/PHPCR/Functional/RenameChildTest.php @@ -0,0 +1,162 @@ +dm = $this->createDocumentManager(array(__DIR__)); + $this->node = $this->resetFunctionalNode($this->dm); + + $parent = new RCTParent(); + $parent->id = '/functional/parent'; + $parent->title = 'Test'; + + $child = new RCTChild(); + $child->parent = $parent; + $child->nodename = 'test'; + $child->title = 'Testchild'; + + $this->dm->persist($parent); + $this->dm->persist($child); + + $this->dm->flush(); + } + + public function testRenameWithOneChild() + { + $this->dm->clear(); + + $parent = $this->dm->find(null, '/functional/parent'); + foreach ($parent->children as $name => $child) { + // just make sure the children collection is initialized + } + + $child = $this->dm->find(null, '/functional/parent/test'); + $this->assertEquals('test', $child->nodename); + + $child->nodename = 'renamed'; + $this->dm->flush(); + + $renamed = $this->dm->find(null, '/functional/parent/renamed'); + + $this->assertNotNull($renamed); + $this->assertEquals('Testchild', $renamed->title); + } + + public function testRenameWithParentChange() + { + $this->dm->clear(); + + $parent = $this->dm->find(null, '/functional/parent'); + foreach ($parent->children as $name => $child) { + // just make sure the children collection is initialized + } + + $child = $this->dm->find(null, '/functional/parent/test'); + $this->assertEquals('test', $child->nodename); + + $child->nodename = 'renamed'; + $parent->title = 'Changed Test'; + + $this->dm->flush(); + + $renamed = $this->dm->find(null, '/functional/parent/renamed'); + + $this->assertNotNull($renamed); + $this->assertEquals('Testchild', $renamed->title); + $this->assertEquals('Changed Test', $renamed->parent->title); + } + + public function testRenameWithTwoChildren() + { + $this->dm->clear(); + + $parent = $this->dm->find(null, '/functional/parent'); + $secondChild = new RCTChild(); + + $secondChild->parent = $parent; + $secondChild->nodename = 'test2'; + + $secondChild->title = 'Testchild 2'; + + $this->dm->persist($secondChild); + $this->dm->flush(); + + $this->dm->clear(); + + $parent = $this->dm->find(null, '/functional/parent'); + foreach ($parent->children as $name => $child) { + // just make sure the children collection is initialized + } + + $child = $this->dm->find(null, '/functional/parent/test'); + $this->assertEquals('test', $child->nodename); + + $child->nodename = 'renamed'; + $this->dm->flush(); + + $renamed = $this->dm->find(null, '/functional/parent/renamed'); + + $this->assertNotNull($renamed); + $this->assertEquals('Testchild', $renamed->title); + } +} + +/** + * @PHPCRODM\Document() + */ +class RCTParent +{ + /** @PHPCRODM\Id */ + public $id; + + /** @PHPCRODM\Field(type="string") */ + public $title; + + /** @PHPCRODM\Children */ + public $children; + + /** @PHPCRODM\PreUpdate */ + public function preUpdate() + { + // NOOP + } +} + +/** + * @PHPCRODM\Document() + */ +class RCTChild +{ + /** @PHPCRODM\Id */ + public $id; + + /** @PHPCRODM\ParentDocument */ + public $parent; + + /** @PHPCRODM\Nodename */ + public $nodename; + + /** @PHPCRODM\Field(type="string") */ + public $title; +}