Skip to content

Commit cd20c6a

Browse files
committed
Merge pull request #1259 from malarzm/reuse-embeds-v2
Set parentAssociation on persist and fix embeds by then
2 parents 1e89ade + 85d91b7 commit cd20c6a

2 files changed

Lines changed: 54 additions & 6 deletions

File tree

lib/Doctrine/ODM/MongoDB/UnitOfWork.php

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,7 +2332,6 @@ function ($assoc) { return $assoc['isCascadeMerge']; }
23322332
*
23332333
* @param object $document
23342334
* @param array $visited
2335-
* @param array $insertNow
23362335
*/
23372336
private function cascadePersist($document, array &$visited)
23382337
{
@@ -2348,14 +2347,35 @@ function ($assoc) { return $assoc['isCascadePersist']; }
23482347

23492348
if ($relatedDocuments instanceof Collection || is_array($relatedDocuments)) {
23502349
if ($relatedDocuments instanceof PersistentCollection) {
2350+
if ($relatedDocuments->getOwner() !== $document) {
2351+
$relatedDocuments = $this->fixPersistentCollectionOwnership($relatedDocuments, $document, $class, $mapping['name']);
2352+
}
23512353
// Unwrap so that foreach() does not initialize
23522354
$relatedDocuments = $relatedDocuments->unwrap();
23532355
}
23542356

2355-
foreach ($relatedDocuments as $relatedDocument) {
2357+
$count = 0;
2358+
foreach ($relatedDocuments as $relatedKey => $relatedDocument) {
2359+
if ( ! empty($mapping['embedded'])) {
2360+
list(, $knownParent, ) = $this->getParentAssociation($relatedDocument);
2361+
if ($knownParent && $knownParent !== $document) {
2362+
$relatedDocument = clone $relatedDocument;
2363+
$relatedDocuments[$relatedKey] = $relatedDocument;
2364+
}
2365+
$pathKey = ! isset($mapping['strategy']) || CollectionHelper::isList($mapping['strategy']) ? $count++ : $relatedKey;
2366+
$this->setParentAssociation($relatedDocument, $mapping, $document, $mapping['name'] . '.' . $pathKey);
2367+
}
23562368
$this->doPersist($relatedDocument, $visited);
23572369
}
23582370
} elseif ($relatedDocuments !== null) {
2371+
if ( ! empty($mapping['embedded'])) {
2372+
list(, $knownParent, ) = $this->getParentAssociation($relatedDocuments);
2373+
if ($knownParent && $knownParent !== $document) {
2374+
$relatedDocuments = clone $relatedDocuments;
2375+
$class->setFieldValue($document, $mapping['name'], $relatedDocuments);
2376+
}
2377+
$this->setParentAssociation($relatedDocuments, $mapping, $document, $mapping['name']);
2378+
}
23592379
$this->doPersist($relatedDocuments, $visited);
23602380
}
23612381
}
@@ -2507,7 +2527,20 @@ public function unscheduleOrphanRemoval($document)
25072527
}
25082528
}
25092529

2510-
private function fixPersistentCollectionOwnership(PersistentCollection $coll, $document, $class, $propName)
2530+
/**
2531+
* Fixes PersistentCollection state if it wasn't used exactly as we had in mind:
2532+
* 1) sets owner if it was cloned
2533+
* 2) clones collection, sets owner, updates document's property and, if necessary, updates originalData
2534+
* 3) NOP if state is OK
2535+
* Returned collection should be used from now on (only important with 2nd point)
2536+
*
2537+
* @param PersistentCollection $coll
2538+
* @param object $document
2539+
* @param ClassMetadata $class
2540+
* @param string $propName
2541+
* @return PersistentCollection
2542+
*/
2543+
private function fixPersistentCollectionOwnership(PersistentCollection $coll, $document, ClassMetadata $class, $propName)
25112544
{
25122545
$owner = $coll->getOwner();
25132546
if ($owner === null) { // cloned
@@ -2519,8 +2552,10 @@ private function fixPersistentCollectionOwnership(PersistentCollection $coll, $d
25192552
$newValue = clone $coll;
25202553
$newValue->setOwner($document, $class->fieldMappings[$propName]);
25212554
$class->reflFields[$propName]->setValue($document, $newValue);
2522-
// @todo following line should be superfluous once collections are stored in change sets
2523-
$this->setOriginalDocumentProperty(spl_object_hash($document), $propName, $newValue);
2555+
if ($this->isScheduledForUpdate($document)) {
2556+
// @todo following line should be superfluous once collections are stored in change sets
2557+
$this->setOriginalDocumentProperty(spl_object_hash($document), $propName, $newValue);
2558+
}
25242559
return $newValue;
25252560
}
25262561
return $coll;

tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedTest.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Doctrine\ODM\MongoDB\Tests\Functional;
44

5+
use Doctrine\Common\Collections\ArrayCollection;
56
use Documents\Address;
67
use Documents\Profile;
78
use Documents\Phonenumber;
@@ -539,8 +540,12 @@ public function testWhenCopyingManyEmbedSubDocumentsFromOneDocumentToAnotherWill
539540
$test2 = new ChangeEmbeddedIdTest();
540541
$test2->embedMany = $test1->embedMany; //using clone will work
541542
$this->dm->persist($test2);
543+
544+
$this->assertNotSame($test1->embedMany->first(), $test2->embedMany->first());
545+
542546
$this->dm->flush();
543547

548+
544549
//do some operations on test1
545550
$this->dm->persist($test1);
546551
$this->dm->flush();
@@ -564,6 +569,9 @@ public function testReusedEmbeddedDocumentsAreClonedInFact()
564569

565570
$this->dm->persist($test1);
566571
$this->dm->persist($test2);
572+
573+
$this->assertNotSame($test1->embed, $test2->embed);
574+
567575
$this->dm->flush();
568576

569577
$this->assertNotSame($test1->embed, $test2->embed);
@@ -593,7 +601,12 @@ class ChangeEmbeddedIdTest
593601
/**
594602
* @ODM\EmbedMany(targetDocument="EmbeddedDocumentWithId")
595603
*/
596-
public $embedMany = array();
604+
public $embedMany;
605+
606+
public function __construct()
607+
{
608+
$this->embedMany = new ArrayCollection();
609+
}
597610
}
598611

599612
/**

0 commit comments

Comments
 (0)