From 8ea1d3825fddaaa4e2c8dae34debf9b5c7824229 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 8 Aug 2010 17:13:03 +0200 Subject: [PATCH] DDC-735 - Fix PersistentCollection::remove() and PersistentCollection::removeElement() behaving differently with regards to orphan removal --- lib/Doctrine/ORM/PersistentCollection.php | 12 +- .../ORM/Functional/Ticket/DDC735Test.php | 121 ++++++++++++++++++ 2 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index c2e4207b7..971d1f72c 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -379,9 +379,15 @@ final class PersistentCollection implements Collection }*/ $this->initialize(); - $result = $this->coll->removeElement($element); - $this->changed(); - return $result; + $removed = $this->coll->removeElement($element); + if ($removed) { + $this->changed(); + if ($this->association !== null && $this->association->isOneToMany() && + $this->association->orphanRemoval) { + $this->em->getUnitOfWork()->scheduleOrphanRemoval($removed); + } + } + return $removed; } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php new file mode 100644 index 000000000..76d3720c9 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC735Test.php @@ -0,0 +1,121 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC735Product'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\\DDC735Review') + )); + } catch(\Exception $e) { + + } + } + + public function testRemoveElement_AppliesOrphanRemoval() + { + // Create a product and its first review + $product = new DDC735Product; + $review = new DDC735Review($product); + + // Persist and flush + $this->_em->persist($product); + $this->_em->flush(); + + // Now you see it + $this->assertEquals(1, count($product->getReviews())); + + // Remove the review + $product->removeReview($review); + $this->_em->flush(); + + // Now you don't + $this->assertEquals(0, count($product->getReviews()), 'count($reviews) should be 0 after removing its only Review'); + + // Refresh + $this->_em->refresh($product); + + // It should still be 0 + $this->assertEquals(0, count($product->getReviews()), 'count($reviews) should still be 0 after the refresh'); + + // Review should also not be available anymore + $this->assertNull($this->_em->find(__NAMESPACE__.'\DDC735Review', $review->getId())); + } +} + +/** + * @Entity + */ +class DDC735Product +{ + /** + * @Id @Column(type="integer") @GeneratedValue + */ + protected $id; + + /** + * @OneToMany( + * targetEntity="DDC735Review", + * mappedBy="product", + * cascade={"persist"}, + * orphanRemoval=true + * ) + */ + protected $reviews; + + public function __construct() + { + $this->reviews = new ArrayCollection; + } + + public function getReviews() + { + return $this->reviews; + } + + public function addReview(DDC735Review $review) + { + $this->reviews->add($review); + } + + public function removeReview(DDC735Review $review) + { + $this->reviews->removeElement($review); + } +} + +/** + * @Entity + */ +class DDC735Review +{ + /** + * @Id @Column(type="integer") @GeneratedValue + */ + protected $id; + + /** + * @ManyToOne(targetEntity="DDC735Product", inversedBy="reviews") + */ + protected $product; + + public function __construct(DDC735Product $product) + { + $this->product = $product; + $product->addReview($this); + } + + public function getId() + { + return $this->id; + } +} \ No newline at end of file