I'm trying to detect changes in a many-to-many relation in an onFlush
event.
If new entities are added to the relation or the relation is updated (always keeping an element), I can detect changes using $unitOfWork->getScheduledCollectionUpdates()
and then check for getInsertDiff()
or getDeleteDiff()
. So far so good.
The problem comes when I take all the entities out of the relation: "There were two related entities before but there are NO related entities now."
When the relation is left empty I can access $unitOfWork->getScheduledCollectionDeletions()
, but there is no way of knowing which entities were deleted:
getDeleteDiff()
for this collections doesn't tell anything.
getSnapshot()
doesn't tell me which entities were there before
How should I know which entities were taken out of the many-to-many relation?
I've added a Gist with the full implementation: everything works ok (it may need some optimization) except $uow->getScheduledCollectionDeletions()
(line 101)
https://gist.github.com/eillarra/5127606
The cause of this problem is twofold:
1) When the method clear()
is called on a Doctrine\ORM\PersistentCollection
, it will:
- clear its internal collection of entities.
- call
scheduleCollectionDeletion()
on the Doctrine\ORM\UnitOfWork
.
- take a new snapshot of itself.
Number 2 is the reason your collection shows up in $uow->getScheduledCollectionDeletions()
(and not in $uow->getScheduledCollectionUpdates()
). Number 3 is the reason why you cannot determine what was in the collection before it was cleared.
2) When using the Symfony2 Form component, specifically the ChoiceType
or CollectionType
types in combination with the option multiple
, that clear()
method will get called when all entities should be removed from the collection.
This is due to the MergeDoctrineCollectionListener
which is added here:
https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php#L55
This is done as optimization: It's faster to clear a collection this way, in stead of checking which entities should be removed from it.
I can think of two possible solutions:
1) Create a fork symfony/symfony
and implement an option in order to not add the MergeDoctrineCollectionListener
. Maybe something like no_clear
to prevent the listener from being added. This won't introduce a BC break and would solve your problem because the clear()
method of a collection won't get called when all entities should be removed.
2) Redesign your counter: Maybe also listen to the OnLoad
event which can count the amount of entities in the collection at the time it's fetched from the db. That way your OnFlush
listener can use that number to know how many entities where removed from the collection when it was cleared.