From 121158f92c9e5c5ed52accafeaaca8e3412cf9ec Mon Sep 17 00:00:00 2001 From: Kyron Taylor Date: Sat, 3 Aug 2024 16:49:18 +0100 Subject: [PATCH 1/2] GH11551 - fix OneToManyPersister::deleteEntityCollection when using single-inheritence entity parent as targetEntity. When using the parent entity for a single-inheritence table as the targetEntity for a property, the discriminator value should be all of the values in the discriminator map. OneToManyPersister::deleteEntityCollection has been amended to reflect this. --- .../Collection/OneToManyPersister.php | 12 +- .../ORM/Functional/Ticket/GH11501Test.php | 120 ++++++++++++++++++ 2 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 tests/Tests/ORM/Functional/Ticket/GH11501Test.php diff --git a/src/Persisters/Collection/OneToManyPersister.php b/src/Persisters/Collection/OneToManyPersister.php index 6769acca909..aed37556bc7 100644 --- a/src/Persisters/Collection/OneToManyPersister.php +++ b/src/Persisters/Collection/OneToManyPersister.php @@ -13,10 +13,13 @@ use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Utility\PersisterHelper; +use function array_fill; +use function array_keys; use function array_merge; use function array_reverse; use function array_values; use function assert; +use function count; use function implode; use function is_int; use function is_string; @@ -194,9 +197,12 @@ private function deleteEntityCollection(PersistentCollection $collection): int if ($targetClass->isInheritanceTypeSingleTable()) { $discriminatorColumn = $targetClass->getDiscriminatorColumn(); - $statement .= ' AND ' . $discriminatorColumn['name'] . ' = ?'; - $parameters[] = $targetClass->discriminatorValue; - $types[] = $discriminatorColumn['type']; + $discriminatorValues = $targetClass->discriminatorValue ? [$targetClass->discriminatorValue] : array_keys($targetClass->discriminatorMap); + $statement .= ' AND ' . $discriminatorColumn['name'] . ' IN (' . implode(', ', array_fill(0, count($discriminatorValues), '?')) . ')'; + foreach ($discriminatorValues as $discriminatorValue) { + $parameters[] = $discriminatorValue; + $types[] = $discriminatorColumn['type']; + } } $numAffected = $this->conn->executeStatement($statement, $parameters, $types); diff --git a/tests/Tests/ORM/Functional/Ticket/GH11501Test.php b/tests/Tests/ORM/Functional/Ticket/GH11501Test.php new file mode 100644 index 00000000000..715137d43af --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH11501Test.php @@ -0,0 +1,120 @@ +setUpEntitySchema([ + GH11501AbstractTestEntity::class, + GH11501TestEntityOne::class, + GH11501TestEntityTwo::class, + GH11501TestEntityHolder::class, + ]); + } + + /** + * @throws ORMException + */ + public function testDeleteOneToManyCollectionWithSingleTableInheritance(): void + { + $testEntityOne = new GH11501TestEntityOne(); + $testEntityTwo = new GH11501TestEntityTwo(); + $testEntityHolder = new GH11501TestEntityHolder(); + + $testEntityOne->testEntityHolder = $testEntityHolder; + $testEntityHolder->testEntities->add($testEntityOne); + + $testEntityTwo->testEntityHolder = $testEntityHolder; + $testEntityHolder->testEntities->add($testEntityTwo); + + $em = $this->getEntityManager(); + $em->persist($testEntityOne); + $em->persist($testEntityTwo); + $em->persist($testEntityHolder); + $em->flush(); + + $testEntityHolder->testEntities = new ArrayCollection(); + $em->persist($testEntityHolder); + $em->flush(); + $em->refresh($testEntityHolder); + + static::assertEmpty($testEntityHolder->testEntities->toArray(), 'All records should have been deleted'); + } +} + + + +/** + * @ORM\Entity + * @ORM\Table(name="one_to_many_single_table_inheritance_test_entities_parent_join") + * @ORM\InheritanceType("SINGLE_TABLE") + * @ORM\DiscriminatorColumn(name="type", type="string") + * @ORM\DiscriminatorMap({"test_entity_one"="GH11501TestEntityOne", "test_entity_two"="GH11501TestEntityTwo"}) + */ +class GH11501AbstractTestEntity +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue + * + * @var int + */ + public $id; + + /** + * @ORM\ManyToOne(targetEntity="GH11501TestEntityHolder", inversedBy="testEntities") + * @ORM\JoinColumn(name="test_entity_holder_id", referencedColumnName="id") + * + * @var GH11501TestEntityHolder + */ + public $testEntityHolder; +} + + +/** @ORM\Entity */ +class GH11501TestEntityOne extends GH11501AbstractTestEntity +{ +} + +/** @ORM\Entity */ +class GH11501TestEntityTwo extends GH11501AbstractTestEntity +{ +} + +/** @ORM\Entity */ +class GH11501TestEntityHolder +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue + * + * @var int + */ + public $id; + + /** + * @ORM\OneToMany(targetEntity="GH11501AbstractTestEntity", mappedBy="testEntityHolder", orphanRemoval=true) + * + * @var Collection + */ + public $testEntities; + + public function __construct() + { + $this->testEntities = new ArrayCollection(); + } +} From 2707b09a07e00097bfef81f3ee35c6435586e0a6 Mon Sep 17 00:00:00 2001 From: gitbugr Date: Sat, 3 Aug 2024 21:38:49 +0100 Subject: [PATCH 2/2] fix spacing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Grégoire Paris --- src/Persisters/Collection/OneToManyPersister.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Persisters/Collection/OneToManyPersister.php b/src/Persisters/Collection/OneToManyPersister.php index aed37556bc7..1e032e99b49 100644 --- a/src/Persisters/Collection/OneToManyPersister.php +++ b/src/Persisters/Collection/OneToManyPersister.php @@ -201,7 +201,7 @@ private function deleteEntityCollection(PersistentCollection $collection): int $statement .= ' AND ' . $discriminatorColumn['name'] . ' IN (' . implode(', ', array_fill(0, count($discriminatorValues), '?')) . ')'; foreach ($discriminatorValues as $discriminatorValue) { $parameters[] = $discriminatorValue; - $types[] = $discriminatorColumn['type']; + $types[] = $discriminatorColumn['type']; } }