From 7a87d62a96d3b838570d53c1d2b3b4c2722e6878 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Tue, 28 Feb 2023 08:04:59 +0000 Subject: [PATCH] Failing test: UoW unable to break cycles when removing entities without DB-level cascade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a failing test case for #5665. In this example, we have a cyclic association between three entities. All associations are NULLable, so the ORM is able to perform the INSERT operation: The cycle can be broken by scheduling an "extra UPDATE" in the UoW. However, the UoW is unable to perform the remove operation. Cyclic references by the foreign keys in the database prevent removal of two of the entities. If the ORM were able to detect this case and perform an UPDATE _before_ the DELETE, the test would pass. As a workaround, `@JoinColumn(onDelete="CASCADE")` can be used. That way, the DBMS will make this UPDATE just in time and without ORM support – but that seems not to be what the OP of #5665 asked for. --- .../Ticket/GH5665CommitOrderTest.php | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 tests/Tests/ORM/Functional/Ticket/GH5665CommitOrderTest.php diff --git a/tests/Tests/ORM/Functional/Ticket/GH5665CommitOrderTest.php b/tests/Tests/ORM/Functional/Ticket/GH5665CommitOrderTest.php new file mode 100644 index 00000000000..487fe371bb3 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH5665CommitOrderTest.php @@ -0,0 +1,116 @@ +setUpEntitySchema([GH5665CommitOrderItem::class, GH5665CommitOrderSubitem::class]); + } + + public function testIssue(): void + { + $item = new GH5665CommitOrderItem(); + $sub1 = new GH5665CommitOrderSubitem(); + $sub2 = new GH5665CommitOrderSubitem(); + $item->addItem($sub1); + $item->addItem($sub2); + $item->featuredItem = $sub2; + $this->_em->persist($item); + $this->_em->flush(); + + $this->expectNotToPerformAssertions(); + + $this->_em->remove($item); + $this->_em->flush(); + } +} + +/** + * @ORM\Entity + */ +class GH5665CommitOrderItem +{ + /** + * @ORM\Column(name="id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + * + * @var int + */ + public $id; + + /** + * @ORM\OneToMany(targetEntity="GH5665CommitOrderSubitem", mappedBy="item", cascade={"all"}, orphanRemoval=true) + * + * @var Collection + */ + public $items; + + /** + * @ORM\ManyToOne(targetEntity="GH5665CommitOrderSubitem") + * + * Adding the following would make the test pass, since it shifts responsibility for + * NULLing references to the DB layer. The #5665 issue is about the request that this + * happen on the ORM level. + * > @-ORM\JoinColumn(onDelete="SET NULL") + * + * @var GH5665CommitOrderSubitem + */ + public $featuredItem; + + public function __construct() + { + $this->items = new ArrayCollection(); + } + + public function addItem(GH5665CommitOrderSubitem $item) + { + $this->items[] = $item; + $item->item = $this; + } +} + +/** + * @ORM\Entity + */ +class GH5665CommitOrderSubitem +{ + /** + * @ORM\Column(name="id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + * + * @var integer + */ + public $id; + + /** + * @var GH5665CommitOrderItem + * + * @ORM\ManyToOne(targetEntity="GH5665CommitOrderItem", inversedBy="items") + */ + public $item; +}