mirror of
https://github.com/doctrine/orm.git
synced 2026-03-23 22:42:18 +01:00
Since v2.19.5 $em->remove($child) doesn't work if called prior to creating another child and $em->persist($parent)
#7369
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @mislavjakopovic on GitHub (May 10, 2024).
BC Break Report
Summary
Following scenario produces a breaking change after upgrading to
doctrine/orm: "2.19.5":OneToManyrelation. Parent relation is configured tocascade: ['persist', 'remove']$entityManager->remove($child)$parentRepository->find()$entityManager->persist($parent)Persisting a Parent entity directly instead of Child is not the best approach, but afaik there is nowhere written that
it is forbidden. I believe there are some use cases where this can actually be quite practical, instead of having persist child on 10 places in code, a simple persist of parent works here more elegant. It is not the best pattern, but I believe it is a valid behavior, otherwise we wouldn't be even allowed to call persist on already loaded entity manually.
The issue comes from a change where we would remove entity from
identityMaponly upon executing deletion queries (UnitOfWork::executeDeletions) rather than straight away (UnitOfWork::scheduleForDelete): https://github.com/doctrine/orm/pull/11428Example
Previous Behavior (v2.19.4)
After calling
$entityManager->flush()Child entity was deleted and a new Child was created and assigned to Parent.Current Behavior (v2.19.5)
After calling
$entityManager->flush()Child entity is not deleted and a new Child is created and assigned to parent (remove had no effect)How to recreate
Made a test case repository https://github.com/mislavjakopovic/doctrine-orm-issue-11448/ with example code which can be ran quickly via
docker-compose.Workarounds
In
doctrine/orm: "2.19.5"for aboveremove()to have effect we need to either:flush()call right afterremove()persist($newPage), notpersist(book)persist(book)@xificurk commented on GitHub (May 12, 2024):
Thanks for the example repo. The main issue is that the
Pageentity is removed without updating its existing association toBookentity, i.e. the removedPageis still contained inBook::$pagescollection.Why the Page rows are deleted from database on <=2.19.4 :
When
Pageentity is removed,Book::$pagescollection is not updated, but it is also not initialized. The collection gets initialized only after Book::addPage() call. Because of the original bug (fixed in https://github.com/doctrine/orm/pull/11428) the collection will contain a references to new instances of removed entities, and because they are inmanagedstate, they will be ignored by cascadingpersist($book)call. The flush results in inconsistent state, where odd-idPageentities are deleted from database, but their instances are kept in identity map andBook::$pagescollections. See PR with expanded output that iterates over the contents ofBook::$pages: https://github.com/mislavjakopovic/doctrine-orm-issue-11448/pull/3Furthermore, the entities are deleted from database only thanks to the fact that
Book::$pagescollections are not initialized at the moment ofremove()call. If those collections are initialized before that, they will not get deleted: https://github.com/mislavjakopovic/doctrine-orm-issue-11448/pull/2The proper fix is to take care of any existing associations before removing the entity: https://github.com/mislavjakopovic/doctrine-orm-issue-11448/pull/1
@greg0ire commented on GitHub (May 28, 2024):
Closing since developers are responsible for maintaining both sides of the association. I think that's stated somewhere in the docs.