Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Merge support #205

Merged
merged 1 commit into from

5 participants

@lsmith77
Owner

it basically works. the tests are not yet sufficient and there are some cases not yet covered. also there is some redundant code that needs to be refactored.

open questions:
1) should he have special handling to prevent overwriting child properties with null?
2) should we have special handling to prevent overwriting node/nodename/version name/version created/locale with null?

http://doctrine-project.org/jira/browse/PHPCR-13

@lsmith77 lsmith77 referenced this pull request
Closed

Merge support #201

@dantleech
Collaborator

Hi, I'm having a problem with this PR.

daniel@dan-x220 ~/www/DCMS :( $ ./app/console cache:warmup --env=prod 
Warming up the cache for the prod environment with debug false
PHP Catchable fatal error:  Argument 1 passed to Doctrine\ODM\PHPCR\Mapping\Driver\AnnotationDriver::getCascadeMode() must be of the type array, integer given, called in /home/daniel/www/DCMS/vendor/doctrine/phpcr-odm/lib/Doctrine/ODM/PHPCR/Mapping/Driver/AnnotationDriver.php on line 134 and defined in /home/daniel/www/DCMS/vendor/doctrine/phpcr-odm/lib/Doctrine/ODM/PHPCR/Mapping/Driver/AnnotationDriver.$hp on line 211

Basically, getCascadeMode(array $arrayList) is being passed 0 - which is the cascade mode integer when there is no cascade defined, I'm not 100% sure how it gets here -- but it comes from AbstractFile. So I think there is some inheritance stuff going on which is incompatible with oevrriding the array of cascade modes in the annotation with an integer ...

@lsmith77
Owner

hmm i dont remember having fixed that issue .. but it doesnt seem to be there now. i did do a few rebases of this PR.

@dantleech
Collaborator

I think it should still be there, unless its fixed by the merge stuff, but I don't see how it could be, might be due to a nuance in my setup. I will investigate further tomorrow ...

@dantleech
Collaborator

yeah, still the same error, now at line 214

@lsmith77
Owner

hmm this all works nicely for me .. the annotation parser takes care of splitting the values into an array. does the test suite run through for you? what version of doctrine common are you using? maybe this is an issue in the annotation parser version .. ?

@dantleech
Collaborator

Ah yes, I bumped Doctrine\Common to latest and that works nicely. cheers.

@lsmith77
Owner

hmm which means we may need to bump our dependency ..

@rat4m3n

Yeah, I got line 214 complaining too.

@lsmith77
Owner

i guess we need to find out which version of common is required and decide if we want to bump the requirement or add code here to ensure that we have an array. @beberlei do you have an idea?

@rat4m3n

i have checked and kept downgrading common in my composer and actually none of the previous versions work... including attempt for dev-master.

@lsmith77
Owner

it works fine for me with Doctrine Common 2.3.0 (doctrine/common@bb0aebb)

@rat4m3n

OK. 2.3.0 still failing for me .. but aditional check:

    if (!$cascadeList) {
        return 0;
    }

Has fixed this for me there. Not sure if this is 100% correct fix (probably not).

@lsmith77
Owner

so the tests are failing for you?

@rat4m3n

Well my composer / application is failing even with doctrine/common 2.3.0.

Unless I add this condition before the loop in AnnotationDriver.php around the line 214.

@lsmith77
Owner

i am asking because i would like to know if your application is simply doing something different than we currently have covered in the test suite. if so it would be good if you could create a failing test case.

@rat4m3n

This actually makes me think... I dont set titles..... for my SimpleBlock nodes...

@lsmith77
Owner

i just pushed some additional tests to master (64dfdc7) and i cannot reproduce the issue. if the issue persists with your code, can you please open a new ticket ideally with either a snippet of your code pasted or with a failing test?

@rat4m3n

OK... Its failing on having ParentDocument instance:

Catchable Fatal Error: Argument 1 passed to Doctrine\ODM\PHPCR\Mapping\Driver\AnnotationDriver::getCascadeMode() must be of the type array, integer given, called
in [...]/vendor/doctrine/phpcr-odm/lib/Doctrine/ODM/PHPCR/Mapping/Driver/AnnotationDriver.php on line 134
and defined
in [...]/vendor/doctrine/phpcr-odm/lib/Doctrine/ODM/PHPCR/Mapping/Driver/AnnotationDriver.php line 211

@lsmith77
Owner

like i said .. i cannot reproduce this issue with doctrine common 2.3.0 :-/

lib/Doctrine/ODM/PHPCR/UnitOfWork.php
((161 lines not shown))
+ $mapping['filter']
+ );
+ $prop->setValue($managedCopy, $managedCol);
+ $this->originalData[$managedOid][$name] = $managedCol;
+ }
+ if ($mapping['cascade'] & ClassMetadata::CASCADE_MERGE > 0) {
+ $managedCol->initialize();
+ if (!$managedCol->isEmpty()) {
+ // clear managed collection, in casacadeMerge() the collection is filled again.
+ $managedCol->unwrap()->clear();
+ $managedCol->setDirty(true);
+ }
+ }
+ } elseif ($class->parentMapping === $name) {
+ $this->doMergeSingleDocumentProperty($managedCopy, $document, $prop, $class->parentMappingData);
+ } elseif (isset($class->localeMapping[$name])
@lsmith77 Owner
lsmith77 added a note

prevent setting locale, version name, version created, node or nodename to null if it was set in the managed copy already .. does this make sense?

@dbu Collaborator
dbu added a note

do we merge 2 objects or just update the db from a non-tracked object? version name and version created are readonly fields, node too. if nodename is null this means we have an id set, otherwise we would not even find the right document to merge with? if so then yes, name can be kept. locale is the most dangerous one. keeping the one from the db makes probably sense, unless specified explicitly it would be request locale, which would be the default anyways.

@lsmith77 Owner
lsmith77 added a note

just FYI

there are basically 3 scenarios:
1) the supplied document with an existing version
a) the existing version was fetched from the db
b) the existing version was not fetched from the db (ie. it may have been flushed to the db, but it was created in this request)
2) the supplied document was never previously persisted and therefore its effectively just a persist call

the tricky topic is really only case 1) .. and here usually only a) is relevant for stuff like node/version name/version created ..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/Doctrine/ODM/PHPCR/UnitOfWork.php
((131 lines not shown))
+ $managedCopy,
+ $mapping['filter'],
+ $mapping['fetchDepth']
+ );
+ $prop->setValue($managedCopy, $managedCol);
+ $this->originalData[$managedOid][$name] = $managedCol;
+ }
+ if ($mapping['cascade'] & ClassMetadata::CASCADE_MERGE > 0) {
+ $managedCol->initialize();
+ if (!$managedCol->isEmpty()) {
+ // clear managed collection, in casacadeMerge() the collection is filled again.
+ $managedCol->unwrap()->clear();
+ $managedCol->setDirty(true);
+ }
+ }
+ } elseif (isset($class->referrersMappings[$name])) {
@lsmith77 Owner
lsmith77 added a note

lots of redundant code .. maybe pass a closure to build the collection to a generic method?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/Doctrine/ODM/PHPCR/UnitOfWork.php
((103 lines not shown))
+ );
+ $prop->setValue($managedCopy, $managedCol);
+ $this->originalData[$managedOid][$name] = $managedCol;
+ }
+ if ($mapping['cascade'] & ClassMetadata::CASCADE_MERGE > 0) {
+ $managedCol->initialize();
+ if (!$managedCol->isEmpty()) {
+ // clear managed collection, in casacadeMerge() the collection is filled again.
+ $managedCol->unwrap()->clear();
+ $managedCol->setDirty(true);
+ }
+ }
+ }
+ } elseif (isset($class->childMappings[$name])) {
+ $this->doMergeSingleDocumentProperty($managedCopy, $document, $prop, $class->childMappings[$name]);
+ } elseif (isset($class->childrenMappings[$name])) {
@lsmith77 Owner
lsmith77 added a note

lots of redundant code .. maybe pass a closure to build the collection to a generic method?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/Doctrine/ODM/PHPCR/UnitOfWork.php
((75 lines not shown))
+ $managedCopy = $class->newInstance();
+ $class->setIdentifierValue($managedCopy, $id);
+ $persist = true;
+ }
+ }
+
+ $managedOid = spl_object_hash($managedCopy);
+
+ // Merge state of $document into existing (managed) document
+ foreach ($class->reflFields as $name => $prop) {
+ if ( isset($class->associationsMappings[$name])) {
+ $mapping = $class->associationsMappings[$name];
+ if ($mapping['type'] & ClassMetadata::TO_ONE) {
+ $this->doMergeSingleDocumentProperty($managedCopy, $document, $prop, $mapping);
+ } else {
+ $mergeCol = $prop->getValue($document);
@lsmith77 Owner
lsmith77 added a note

lots of redundant code .. maybe pass a closure to build the collection to a generic method?

@dbu Collaborator
dbu added a note

guess its a good place to use a closure. will make the method logic more understandable too i guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/Doctrine/ODM/PHPCR/UnitOfWork.php
@@ -1231,6 +1231,247 @@ private function doRefresh($document, &$visited)
return $document;
}
+ public function merge($document)
+ {
+ $visited = array();
+ return $this->doMerge($document, $visited);
+ }
+
+ private function doMergeSingleDocumentProperty($managedCopy, $document, \ReflectionProperty $prop, array $mapping)
+ {
+ $other = $prop->getValue($document);
+ if (null === $other) {
+ $prop->setValue($managedCopy, null);
@lsmith77 Owner
lsmith77 added a note

this behavior makes sense for ReferenceOne and ParentDocument too i guess .. but it doesnt really make sense for Child imho

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@lsmith77
Owner

ok .. after some more refactoring i think i cut out the redundant code sufficiently. now the question is just about the special handling for null values in the passed document is left as an open question. see the description of the ticket for details.

@dantleech
Collaborator

ok. im still getting the "array/integer getCascadeMode" problem, and the worst thing is that I cannot reproduce it in a unit test -- it only happens in prod mode. I am using Doctrine Commons latest, and I don't understand why it was working for me earlier. But I think I see the problem, maybe:

  1. Folder and File extend AbstractFile
  2. Metadata for Folder is loaded, the reflection property "parent" on abstract class is set to the cascade mode of the Folder.
  3. The File metadata is loaded, and here the value of the reflection property "parent" is evaluated. It evaluates to an integer.

It seems to be that the File property for $parent is the same instance as that of Folder. I.e. the property comes from the same AbstractFile instance. So, when the metadata for Folder is set, it sets the instance associated with AbstractFile, which is used by the two child classes.

In prod mode the Annotation reader is FileCacheReader, whilst in dev it is AnnotationReader. I'm guessing the problem is coming from there, but havn't confirmed it yet. Anyway, have been working on this for a few hours now, so giving up for today.

@lsmith77 can you confirm if you have the problem in prod ? i.e. with the FileCacheReader ?

@lsmith77
Owner

yes .. i can confirm that there is an issue in prod mode .. will have a look

@stof

@lsmith77 the issue is that your are modifying the annotation object to replace the array by an integer: https://github.com/doctrine/phpcr-odm/blob/master/lib/Doctrine/ODM/PHPCR/Mapping/Driver/AnnotationDriver.php#L134

If the annotation is cached with the integer, it will become invalid

@lsmith77
Owner

yeah .. i just found out the same thing .. how about #209?

@stof

I think it should work

@dbu
Collaborator
dbu commented

alright, so do we merge now? or is this still missing something?

@lsmith77
Owner

i am fine with the current state of this PR .. not 100% sure yet about the 2 questions in the descriptions but willing to take the risk that we later discover that we need to change those cases :)

@lsmith77 lsmith77 merged commit e61d766 into master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
1  lib/Doctrine/ODM/PHPCR/DocumentManager.php
@@ -704,7 +704,6 @@ public function merge($document)
throw new \InvalidArgumentException('Parameter $document needs to be an object, '.gettype($document).' given');
}
- throw new \BadMethodCallException(__METHOD__.' not yet implemented');
$this->errorIfClosed();
return $this->getUnitOfWork()->merge($document);
}
View
2  lib/Doctrine/ODM/PHPCR/Mapping/ClassMetadata.php
@@ -1050,8 +1050,6 @@ public function __sleep()
'fieldMappings',
'identifier',
'name',
-// 'collection',
-// 'generatorType',
'generatorOptions',
'idGenerator'
);
View
224 lib/Doctrine/ODM/PHPCR/UnitOfWork.php
@@ -1114,7 +1114,7 @@ private function computeChildChanges($mapping, $child, $parentId, $nodename = nu
switch ($state) {
case self::STATE_NEW:
- if ( !($mapping['cascade'] & ClassMetadata::CASCADE_PERSIST) ) {
+ if (!($mapping['cascade'] & ClassMetadata::CASCADE_PERSIST) ) {
throw CascadeException::newDocumentFound(self::objToStr($child));
}
$nodename = $nodename ? : $mapping['name'];
@@ -1142,7 +1142,7 @@ private function computeReferenceChanges($mapping, $reference)
switch ($state) {
case self::STATE_NEW:
- if ( !($mapping['cascade'] & ClassMetadata::CASCADE_PERSIST) ) {
+ if (!($mapping['cascade'] & ClassMetadata::CASCADE_PERSIST) ) {
throw CascadeException::newDocumentFound(self::objToStr($reference));
}
$this->persistNew($targetClass, $reference);
@@ -1166,7 +1166,7 @@ private function computeReferrerChanges($mapping, $referrer)
switch ($state) {
case self::STATE_NEW:
- if ( !($mapping['cascade'] & ClassMetadata::CASCADE_PERSIST) ) {
+ if (!($mapping['cascade'] & ClassMetadata::CASCADE_PERSIST) ) {
throw CascadeException::newDocumentFound(self::objToStr($referrer));
}
$this->persistNew($targetClass, $referrer);
@@ -1231,6 +1231,224 @@ private function doRefresh($document, &$visited)
return $document;
}
+ public function merge($document)
+ {
+ $visited = array();
+ return $this->doMerge($document, $visited);
+ }
+
+ private function doMergeSingleDocumentProperty($managedCopy, $document, \ReflectionProperty $prop, array $mapping)
+ {
+ if (null === $document) {
+ $prop->setValue($managedCopy, null);
+ } elseif ($mapping['cascade'] & ClassMetadata::CASCADE_MERGE == 0) {
+ if ($this->getDocumentState($document) == self::STATE_MANAGED) {
+ $prop->setValue($managedCopy, $document);
+ } else {
+ $targetClass = $this->dm->getClassMetadata(get_class($document));
+ $id = $targetClass->getIdentifierValues($document);
+ $proxy = $this->createProxy($id, $targetClass->name);
+ $prop->setValue($managedCopy, $proxy);
+ $this->registerDocument($proxy, $id);
+ }
+ }
+ }
+
+ private function cascadeMergeCollection($managedCol, array $mapping)
+ {
+ if ($mapping['cascade'] & ClassMetadata::CASCADE_MERGE > 0) {
+ $managedCol->initialize();
+ if (!$managedCol->isEmpty()) {
+ // clear managed collection, in casacadeMerge() the collection is filled again.
+ $managedCol->unwrap()->clear();
+ $managedCol->setDirty(true);
+ }
+ }
+ }
+
+ private function doMerge($document, array &$visited, $prevManagedCopy = null, $assoc = null)
+ {
+ $oid = spl_object_hash($document);
+ if (isset($visited[$oid])) {
+ return; // Prevent infinite recursion
+ }
+
+ $visited[$oid] = $document; // mark visited
+
+ $class = $this->dm->getClassMetadata(get_class($document));
+
+ // First we assume DETACHED, although it can still be NEW but we can avoid
+ // an extra db-roundtrip this way. If it is not MANAGED but has an identity,
+ // we need to fetch it from the db anyway in order to merge.
+ // MANAGED entities are ignored by the merge operation.
+ if ($this->getDocumentState($document) == self::STATE_MANAGED) {
+ $managedCopy = $document;
+ } else {
+ $id = $class->getIdentifierValue($document);
+ $persist = false;
+
+ if (!$id) {
+ // document is new
+ $managedCopy = $class->newInstance();
+ $persist = true;
+ } else {
+ $managedCopy = $this->getDocumentById($id);
+ if ($managedCopy) {
+ // We have the document in-memory already, just make sure its not removed.
+ if ($this->getDocumentState($managedCopy) == self::STATE_REMOVED) {
+ throw new \InvalidArgumentException('Removed document detected during merge. Cannot merge with a removed document.');
+ }
+ } else {
+ // We need to fetch the managed copy in order to merge.
+ $managedCopy = $this->dm->find($class->name, $id);
+ }
+
+ if ($managedCopy === null) {
+ // If the identifier is ASSIGNED, it is NEW, otherwise an error
+ // since the managed document was not found.
+ if ($class->idGenerator !== ClassMetadata::GENERATOR_TYPE_ASSIGNED) {
+ throw new \InvalidArgumentException('Document not found.');
+ }
+
+ $managedCopy = $class->newInstance();
+ $class->setIdentifierValue($managedCopy, $id);
+ $persist = true;
+ }
+ }
+
+ $managedOid = spl_object_hash($managedCopy);
+
+ // Merge state of $document into existing (managed) document
+ foreach ($class->reflFields as $name => $prop) {
+ $other = $prop->getValue($document);
+ if (($other instanceof PersistentCollection && !$other->isInitialized())
+ || ($other instanceof Proxy && !$other->__isInitialized())
+ ) {
+ // do not merge fields marked lazy that have not been fetched.
+ // keep the lazy persistent collection of the managed copy.
+ continue;
+ }
+
+ if (isset($class->associationsMappings[$name])) {
+ $mapping = $class->associationsMappings[$name];
+ if ($mapping['type'] & ClassMetadata::TO_ONE) {
+ $this->doMergeSingleDocumentProperty($managedCopy, $other, $prop, $mapping);
+ } else {
+ $managedCol = $prop->getValue($managedCopy);
+ if (!$managedCol) {
+ $managedCol = new ReferenceManyCollection(
+ $this->dm,
+ array(),
+ isset($mapping['targetDocument']) ? $mapping['targetDocument'] : null
+ );
+ $prop->setValue($managedCopy, $managedCol);
+ $this->originalData[$managedOid][$name] = $managedCol;
+ }
+ $this->cascadeMergeCollection($managedCol, $mapping);
+ }
+ } elseif (isset($class->childMappings[$name])) {
+ if (null !== $other) {
+ $this->doMergeSingleDocumentProperty($managedCopy, $other, $prop, $class->childMappings[$name]);
+ }
+ } elseif (isset($class->childrenMappings[$name])) {
+ $mapping = $class->childrenMappings[$name];
+ $managedCol = $prop->getValue($managedCopy);
+ if (!$managedCol) {
+ $managedCol = new ChildrenCollection(
+ $this->dm,
+ $managedCopy,
+ $mapping['filter'],
+ $mapping['fetchDepth']
+ );
+ $prop->setValue($managedCopy, $managedCol);
+ $this->originalData[$managedOid][$name] = $managedCol;
+ }
+ $this->cascadeMergeCollection($managedCol, $mapping);
+ } elseif (isset($class->referrersMappings[$name])) {
+ $mapping = $class->referrersMappings[$name];
+ $managedCol = $prop->getValue($managedCopy);
+ if (!$managedCol) {
+ $managedCol = new ReferrersCollection(
+ $this->dm,
+ $managedCopy,
+ $mapping['referenceType'],
+ $mapping['filter']
+ );
+ $prop->setValue($managedCopy, $managedCol);
+ $this->originalData[$managedOid][$name] = $managedCol;
+ }
+ $this->cascadeMergeCollection($managedCol, $mapping);
+ } elseif ($class->parentMapping === $name) {
+ $this->doMergeSingleDocumentProperty($managedCopy, $other, $prop, $class->parentMappingData);
+ } elseif (isset($class->localeMapping[$name])
+ || isset($class->versionNameField[$name])
+ || isset($class->versionCreatedField[$name])
+ || $class->node !== $name
+ || $class->nodename !== $name
+ ) {
+ if (null !== $other) {
+ $prop->setValue($managedCopy, $other);
+ }
+ } elseif (!$class->isIdentifier($name)) {
+ $prop->setValue($managedCopy, $other);
+ }
+ }
+
+ if ($persist) {
+ $this->persistNew($class, $managedCopy);
+ }
+ }
+
+ if ($prevManagedCopy !== null) {
+ $assocField = $assoc['fieldName'];
+ $prevClass = $this->dm->getClassMetadata(get_class($prevManagedCopy));
+ if ($assoc['type'] & ClassMetadata::TO_ONE) {
+ $prevClass->reflFields[$assocField]->setValue($prevManagedCopy, $managedCopy);
+ } else {
+ $prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->add($managedCopy);
+ if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
+ $class->reflFields[$assoc['mappedBy']]->setValue($managedCopy, $prevManagedCopy);
+ }
+ }
+ }
+
+ // Mark the managed copy visited as well
+ $visited[spl_object_hash($managedCopy)] = true;
+
+ $this->cascadeMerge($document, $managedCopy, $visited);
+
+ return $managedCopy;
+ }
+
+ /**
+ * Cascades a merge operation to associated entities.
+ *
+ * @param object $document
+ * @param object $managedCopy
+ * @param array $visited
+ */
+ private function cascadeMerge($document, $managedCopy, array &$visited)
+ {
+ $class = $this->dm->getClassMetadata(get_class($document));
+ foreach ($class->associationsMappings as $assoc) {
+ if ( $assoc['cascade'] & ClassMetadata::CASCADE_MERGE == 0) {
+ continue;
+ }
+ $related = $class->reflFields[$assoc['fieldName']]->getValue($document);
+ if ($related instanceof Collection || is_array($related)) {
+ if ($related instanceof PersistentCollection) {
+ // Unwrap so that foreach() does not initialize
+ $related = $related->unwrap();
+ }
+ foreach ($related as $relatedDocument) {
+ $this->doMerge($relatedDocument, $visited, $managedCopy, $assoc);
+ }
+ } else if ($related !== null) {
+ $this->doMerge($related, $visited, $managedCopy, $assoc);
+ }
+ }
+ }
+
/**
* Detaches a document from the persistence management. It's persistence will
* no longer be managed by Doctrine.
View
4 tests/Doctrine/Tests/Models/CMS/CmsUser.php
@@ -28,6 +28,10 @@ class CmsUser
public $articles;
/** @PHPCRODM\ReferenceMany(targetDocument="CmsGroup") */
public $groups;
+ /** @PHPCRODM\Children() */
+ public $children;
+ /** @PHPCRODM\Child(name="assistant", cascade="persist") */
+ public $child;
public function getId()
{
View
196 tests/Doctrine/Tests/ODM/PHPCR/Functional/MergeTest.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace Doctrine\Tests\ODM\PHPCR\Functional;
+
+use Doctrine\Tests\Models\CMS\CmsUser;
+use Doctrine\Tests\Models\CMS\CmsArticle;
+use Doctrine\Tests\Models\CMS\CmsTeamUser;
+
+class MergeTest extends \Doctrine\Tests\ODM\PHPCR\PHPCRFunctionalTestCase
+{
+ private $dm;
+
+ public function setUp()
+ {
+ $this->dm = $this->createDocumentManager(array(__DIR__));
+ $this->node = $this->resetFunctionalNode($this->dm);
+ $this->dm->getPhpcrSession()->save();
+ }
+
+ public function testMergeNewDocument()
+ {
+ $user = new CmsUser();
+ $user->username = "beberlei";
+ $user->name = "Benjamin";
+
+ $mergedUser = $this->dm->merge($user);
+
+ $this->assertNotSame($mergedUser, $user);
+ $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $mergedUser);
+ $this->assertEquals("beberlei", $mergedUser->username);
+ $this->assertEquals($this->node->getPath().'/'.$mergedUser->username, $mergedUser->id, "Merged new document should have generated path");
+ $this->assertInstanceOf('Doctrine\ODM\PHPCR\ReferenceManyCollection', $mergedUser->articles);
+ }
+
+ public function testMergeManagedDocument()
+ {
+ $user = new CmsUser();
+ $user->username = "beberlei";
+ $user->name = "Benjamin";
+
+ $this->dm->persist($user);
+ $this->dm->flush();
+
+ $mergedUser = $this->dm->merge($user);
+
+ $this->assertSame($mergedUser, $user);
+ }
+
+ public function testMergeKnownDocument()
+ {
+ $user = new CmsUser();
+ $user->username = "beberlei";
+ $user->name = "Benjamin";
+
+ $this->dm->persist($user);
+ $this->dm->flush();
+ $this->dm->clear();
+
+ $mergedUser = $this->dm->merge($user);
+
+ $this->assertNotSame($mergedUser, $user);
+ $this->assertSame($mergedUser->id, $user->id);
+ }
+
+ public function testMergeRemovedDocument()
+ {
+ $user = new CmsUser();
+ $user->username = "beberlei";
+ $user->name = "Benjamin";
+
+ $this->dm->persist($user);
+ $this->dm->flush();
+
+ $this->dm->remove($user);
+
+ $this->setExpectedException('InvalidArgumentException', 'Removed document detected during merge. Cannot merge with a removed document.');
+ $this->dm->merge($user);
+ }
+
+ public function testMergeWithManagedDocument()
+ {
+ $user = new CmsUser();
+ $user->username = "beberlei";
+ $user->name = "Benjamin";
+
+ $this->dm->persist($user);
+ $this->dm->flush();
+
+ $mergableUser = new CmsUser();
+ $mergableUser->id = $user->id;
+ $mergableUser->username = "jgalt";
+ $mergableUser->name = "John Galt";
+
+ $mergedUser = $this->dm->merge($mergableUser);
+
+ $this->assertSame($mergedUser, $user);
+ $this->assertEquals("jgalt", $mergedUser->username);
+ $this->assertInstanceOf("\PHPCR\NodeInterface", $mergedUser->node);
+ }
+
+ public function testMergeUnknownAssignedId()
+ {
+ $doc = new CmsArticle();
+ $doc->id = "/foo";
+ $doc->name = "Foo";
+
+ $mergedDoc = $this->dm->merge($doc);
+
+ $this->assertNotSame($mergedDoc, $doc);
+ $this->assertSame($mergedDoc->id, $doc->id);
+ }
+
+ public function testMergeWithChild()
+ {
+ $user = new CmsUser();
+ $user->username = "beberlei";
+ $user->name = "Benjamin";
+
+ $teamuser = new CmsTeamUser();
+ $teamuser->username = "jwage";
+ $teamuser->name = "Jonathan Wage";
+ $teamuser->parent = $user;
+ $user->child = $teamuser;
+
+ $this->dm->persist($user);
+ $this->dm->flush();
+
+ $mergableUser = new CmsUser();
+ $mergableUser->username = "jgalt";
+ $mergableUser->name = "John Galt";
+ $mergableUser->id = $user->id;
+
+ $mergedUser = $this->dm->merge($mergableUser);
+
+ $this->assertSame($mergedUser, $user);
+ $this->assertEquals("jgalt", $mergedUser->username);
+ $this->assertEquals("jwage", $mergedUser->child->username);
+
+ $this->dm->flush();
+ $mergedUser->children->count();
+
+ $mergableUser = new CmsUser();
+ $mergableUser->id = $user->id;
+
+ $mergedUser = $this->dm->merge($mergableUser);
+
+ $this->assertSame($mergedUser, $user);
+ $this->assertEquals("jwage", $mergedUser->child->username);
+
+ $this->dm->flush();
+ }
+
+ public function testMergeWithChildren()
+ {
+ $user = new CmsUser();
+ $user->username = "beberlei";
+ $user->name = "Benjamin";
+
+ $teamuser = new CmsTeamUser();
+ $teamuser->username = "jwage";
+ $teamuser->name = "Jonathan Wage";
+ $teamuser->parent = $user;
+
+ $this->dm->persist($user);
+ $this->dm->persist($teamuser);
+ $this->dm->flush();
+
+ $mergableUser = new CmsUser();
+ $mergableUser->username = "jgalt";
+ $mergableUser->name = "John Galt";
+ $mergableUser->id = $user->id;
+
+ $mergedUser = $this->dm->merge($mergableUser);
+
+ $this->assertSame($mergedUser, $user);
+ $this->assertEquals("jgalt", $mergedUser->username);
+ $this->assertCount(1, $mergedUser->children);
+
+ $this->dm->flush();
+ $mergedUser->children->count();
+
+ $mergableUser = new CmsUser();
+ $mergableUser->id = $user->id;
+
+ $mergedUser = $this->dm->merge($mergableUser);
+
+ $this->assertSame($mergedUser, $user);
+ $this->assertTrue($mergedUser->children->isInitialized());
+ $this->assertCount(1, $mergedUser->children);
+
+ $this->dm->flush();
+
+ $mergedUser = $this->dm->find(null, $mergedUser->id);
+ $this->assertCount(1, $mergedUser->children);
+ }
+}
Something went wrong with that request. Please try again.