From 53a5a48aed7d87aa1533c0bcbd72e41b686527d8 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 9 Feb 2014 14:27:23 +0100 Subject: [PATCH] [DDC-2624] Fix bug when persistent collection is cloned and used in a new entity. --- lib/Doctrine/ORM/UnitOfWork.php | 10 +++++- .../Models/ECommerce/ECommerceProduct.php | 8 +++++ .../ORM/Functional/Ticket/DDC2074Test.php | 34 ++++++++++++++++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 7bf6c5a5c..a61630d1f 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -560,7 +560,15 @@ class UnitOfWork implements PropertyChangedListener foreach ($class->reflFields as $name => $refProp) { $value = $refProp->getValue($entity); - if ($class->isCollectionValuedAssociation($name) && $value !== null && ! ($value instanceof PersistentCollection)) { + if ($class->isCollectionValuedAssociation($name) && $value !== null) { + if ($value instanceof PersistentCollection) { + if ($value->getOwner() === $entity) { + continue; + } + + $value = new ArrayCollection($value->getValues()); + } + // If $value is not a Collection then use an ArrayCollection. if ( ! $value instanceof Collection) { $value = new ArrayCollection($value); diff --git a/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php b/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php index f053fd2cb..ba4f2a53d 100644 --- a/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php +++ b/tests/Doctrine/Tests/Models/ECommerce/ECommerceProduct.php @@ -137,6 +137,11 @@ class ECommerceProduct } } + public function setCategories($categories) + { + $this->categories = $categories; + } + public function getCategories() { return $this->categories; @@ -166,6 +171,9 @@ class ECommerceProduct public function __clone() { $this->isCloned = true; + if ($this->categories) { + $this->categories = clone $this->categories; + } } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2074Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2074Test.php index 5538ee3d4..f53857ab3 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2074Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2074Test.php @@ -12,6 +12,12 @@ use Doctrine\Tests\Models\ECommerce\ECommerceProduct; */ class DDC2074Test extends \Doctrine\Tests\OrmFunctionalTestCase { + public function setUp() + { + $this->useModelSet('ecommerce'); + parent::setUp(); + } + public function testShouldNotScheduleDeletionOnClonedInstances() { $class = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceProduct'); @@ -26,4 +32,30 @@ class DDC2074Test extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(0, count($uow->getScheduledCollectionDeletions())); } -} \ No newline at end of file + + public function testSavingClonedPersistentCollection() + { + $product = new ECommerceProduct(); + $category = new ECommerceCategory(); + $category->setName('foo'); + $product->addCategory($category); + + $this->_em->persist($product); + $this->_em->persist($category); + $this->_em->flush(); + + $newProduct = clone $product; + + $this->_em->persist($newProduct); + $this->_em->flush(); + $this->_em->clear(); + + $product1 = $this->_em->find('Doctrine\Tests\Models\ECommerce\ECommerceProduct', $product->getId()); + $product2 = $this->_em->find('Doctrine\Tests\Models\ECommerce\ECommerceProduct', $newProduct->getId()); + + $this->assertCount(1, $product1->getCategories()); + $this->assertCount(1, $product2->getCategories()); + + $this->assertSame($product1->getCategories()->get(0), $product2->getCategories()->get(0)); + } +}