Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update in-memory collections before dispatching the postRemove event #11132

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions lib/Doctrine/ORM/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,8 @@ public function commit($entity = null)
// (first delete entities depending upon others, before deleting depended-upon entities).
if ($this->entityDeletions) {
$this->executeDeletions();
} else {
$this->finalizeCollectionUpdates();
}

// Commit failed silently
Expand All @@ -490,7 +492,12 @@ public function commit($entity = null)
}

$this->afterTransactionComplete();
$this->dispatchPostFlushEvent();
$this->postCommitCleanup($entity);
}

private function finalizeCollectionUpdates(): void
{
// Unset removed entities from collections, and take new snapshots from
// all visited collections.
foreach ($this->visitedCollections as $coid => $coll) {
Expand All @@ -502,10 +509,6 @@ public function commit($entity = null)

$coll->takeSnapshot();
}

$this->dispatchPostFlushEvent();

$this->postCommitCleanup($entity);
}

/** @param object|object[]|null $entity */
Expand Down Expand Up @@ -1317,6 +1320,8 @@ private function executeDeletions(): void
}
}

$this->finalizeCollectionUpdates();

// Defer dispatching `postRemove` events to until all entities have been removed.
foreach ($eventsToDispatch as $event) {
$this->listenersInvoker->invoke(
Expand Down
42 changes: 42 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/EntityListenersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Doctrine\Tests\ORM\Functional;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Event\PostPersistEventArgs;
use Doctrine\ORM\Event\PostRemoveEventArgs;
use Doctrine\ORM\Event\PreFlushEventArgs;
Expand All @@ -12,6 +13,7 @@
use Doctrine\ORM\UnitOfWork;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Doctrine\Tests\Models\Company\CompanyContractListener;
use Doctrine\Tests\Models\Company\CompanyEmployee;
use Doctrine\Tests\Models\Company\CompanyFixContract;
use Doctrine\Tests\Models\Company\CompanyPerson;
use Doctrine\Tests\OrmFunctionalTestCase;
Expand Down Expand Up @@ -266,4 +268,44 @@ public function postRemove(PostRemoveEventArgs $args): void

self::assertSame(2, $listener->invocationCount);
}

public function testPostRemoveCalledAfterAllInMemoryCollectionsHaveBeenUpdated(): void
{
$contract = new CompanyFixContract();
$contract->setFixPrice(2000);

$engineer = new CompanyEmployee();
$engineer->setName('J. Doe');
$engineer->setSalary(50);
$engineer->setDepartment('tech');

$contract->addEngineer($engineer);
$engineer->contracts = new ArrayCollection([$contract]);

$this->_em->persist($contract);
$this->_em->persist($engineer);
$this->_em->flush();

$this->_em->getEventManager()->addEventListener([Events::postRemove], new class ($contract) {
/** @var CompanyFixContract */
private $contract;

public function __construct(CompanyFixContract $contract)
{
$this->contract = $contract;
}

public function postRemove(): void
{
Assert::assertEmpty($this->contract->getEngineers()); // Assert collection has been updated before event was dispatched
Assert::assertFalse($this->contract->getEngineers()->isDirty()); // Collections are clean at this point
}
});

$this->_em->remove($engineer);
$this->_em->flush();

self::assertEmpty($contract->getEngineers());
self::assertFalse($contract->getEngineers()->isDirty());
}
}