diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 4ea56f55b57..104671ec62c 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -25,6 +25,7 @@ use Doctrine\Common\Collections\Selectable; use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\Mapping\ClassMetadata; +use function get_class; /** * A PersistentCollection represents a collection of elements that have persistent state. @@ -565,7 +566,9 @@ public function clear() if ($this->association['isOwningSide'] && $this->owner) { $this->changed(); - $uow->scheduleCollectionDeletion($this); + if (! $this->em->getClassMetadata(get_class($this->owner))->isChangeTrackingDeferredExplicit()) { + $uow->scheduleCollectionDeletion($this); + } $this->takeSnapshot(); } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7761Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7761Test.php new file mode 100644 index 00000000000..53215cf0546 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7761Test.php @@ -0,0 +1,91 @@ +setUpEntitySchema([ + GH7761Entity::class, + GH7761ChildEntity::class, + ]); + + $parent = new GH7761Entity(); + $child = new GH7761ChildEntity(); + $parent->children->add($child); + + $this->_em->persist($parent); + $this->_em->persist($child); + $this->_em->flush(); + $this->_em->clear(); + } + + public function testCollectionClearDoesNotClearIfNotPersisted() : void + { + /** @var GH7761Entity $entity */ + $entity = $this->_em->find(GH7761Entity::class, 1); + $entity->children->clear(); + $this->_em->persist(new GH7761Entity()); + $this->_em->flush(); + + $this->_em->clear(); + + $entity = $this->_em->find(GH7761Entity::class, 1); + self::assertCount(1, $entity->children); + + $this->_em->clear(); + } +} + +/** + * @Entity + * @ChangeTrackingPolicy("DEFERRED_EXPLICIT") + */ +class GH7761Entity +{ + /** + * @Id + * @Column(type="integer") + * @GeneratedValue + */ + public $id; + + /** + * @ManyToMany(targetEntity="Doctrine\Tests\ORM\Functional\Ticket\GH7761ChildEntity", cascade={"all"}) + * @JoinTable(name="gh7761_to_child", + * joinColumns={@JoinColumn(name="entity_id")}, + * inverseJoinColumns={@JoinColumn(name="child_id")} + * ) + */ + public $children; + + public function __construct() + { + $this->children = new ArrayCollection(); + } +} + +/** + * @Entity + * @ChangeTrackingPolicy("DEFERRED_EXPLICIT") + */ +class GH7761ChildEntity +{ + /** + * @Id + * @Column(type="integer") + * @GeneratedValue + */ + public $id; +} diff --git a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php index 38be94522a7..179d6e9d517 100644 --- a/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php +++ b/tests/Doctrine/Tests/ORM/PersistentCollectionTest.php @@ -3,6 +3,7 @@ namespace Doctrine\Tests\ORM; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\UnitOfWork; use Doctrine\Tests\Mocks\ConnectionMock; @@ -264,4 +265,25 @@ public function testWillNotMarkCollectionAsDirtyAfterInitializationIfNoElementsW self::assertTrue($this->collection->isInitialized()); self::assertFalse($this->collection->isDirty()); } + + public function testModifyUOWForDeferredImplicitOwnerOnClear() : void + { + $unitOfWork = $this->createMock(UnitOfWork::class); + $unitOfWork->expects(self::once())->method('scheduleCollectionDeletion'); + $this->_emMock->setUnitOfWork($unitOfWork); + + $this->collection->clear(); + } + + public function testDoNotModifyUOWForDeferredExplicitOwnerOnClear() : void + { + $unitOfWork = $this->createMock(UnitOfWork::class); + $unitOfWork->expects(self::never())->method('scheduleCollectionDeletion'); + $this->_emMock->setUnitOfWork($unitOfWork); + + $classMetaData = $this->_emMock->getClassMetadata(ECommerceCart::class); + $classMetaData->setChangeTrackingPolicy(ClassMetadataInfo::CHANGETRACKING_DEFERRED_EXPLICIT); + + $this->collection->clear(); + } }