-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
No ForeignKeyConstraintViolationException exception when trying to remove an entity #10752
Comments
#10486 implemented that change |
Looking at the code in #10753, why should it throw? Where is the foreign key? |
Hi @mpdude, Configuration is the same (in terms of relation between these two entities), and there are two foreign keys in the db. |
Yes, that’s the foreign keys in the m2m association table. But where is the foreign key restricting removal of the element from the many-to-many collection? |
@mpdude considering on Somehow between P.S. Also, trying to delete via MySQL CLI resulted in the error, so there's a constraint preventing removing the record. |
Ok, I think I begin to understand the situation. You have an unidirectional Already before a951369, when you directly remove a orm/lib/Doctrine/ORM/UnitOfWork.php Lines 960 to 967 in 76b8a21
The intention is that the collection should no longer point to/contain an entity that no longer exists. pre a951369, this collection update went unnoticed for the current This is why you got the The problem with that was reported as #10485: After the The a951369 change makes sure to include the collection update in the same This is why you no longer get the exception: The ORM now removes the join table row before the Out of curiosity, I removed the lines shown above. The only tests that fail are those added in #10486. So, it seems that we did not have any tests at all covering this collection-updating code. To make matters worse and the situation a bit more complicated, the tests from #10753 pass even with the a951369 change if you This time, the UoW does not know about the m2m collection on the Now, at that point, I don't know how to proceed. I think it is necessary to agree on how the ORM should behave, whether it should update the collections or not. I've checked the documentation on this, but found nothing really distinct. I am not sure if that might mean the ORM would have to implement configuration or switches to support One thing I would assume is that, at least, the behaviour should be consistent regardless of whether a collection has been loaded into memory or not. 🤷🏻 |
One thing is unclear to me about the current behavior. If there is a foreign key pointing to the entity being removed, has is it even possible to remove the referenced entity? I would expect the RDBMS to make that impossible. |
Depending on the database level CASCADE/RESTRICT behavior, the RDBMS would abort or remove the n/m table row. The point is that Doctrine removes the to-be-deleted entity from the collection and flushes that first, so the DB level foreign key never notices a problem. |
So right now, there is no exception, and nothing gets removed, or there is an exception, just not the expected one? @jakubtobiasz didn't mention an exception, so I guess it cascades right now? |
Ok this means Doctrine removes the n-m table row before deleting the promotion row, whereas before, it forced users to be explicit about this. Well if ensuring a consistent behavior is too complicated, let's say that the behavior is undefined: it may throw or may succeed, and if users want a reliable result, then they should explicitly delete the link between Order and Promotion. If you cannot find documentation stating what should happen, then it is undefined, and if you want you can contribute a documentation paragraph making it explicitly undefined, and stating how to be sure not to get an exception (here, I think it's by removing the promotion from the collection of promotions in the order object). Maybe the best place to document that would be here: https://www.doctrine-project.org/projects/doctrine-orm/en/stable/reference/working-with-associations.html#removing-associations It can always been changed afterwards if you somehow manage to get a consistent behavior for this (removing the target entity of an m2m unidirectional association, i.e. removing entity on the inverse side). Sounds fair?
I don't know either, maybe another maintainer will. |
For the first time, I've noticed the following documentation section: orm/docs/en/reference/working-with-objects.rst Lines 289 to 294 in 76b8a21
How do you interpret that? |
The first sentence makes it clear what is suppose to happen. According to it, there should never have been a |
Well, yes, that's pretty unambiguous. But.
@beberlei maybe you have thoughts on that? (sorry for pinging) |
Maybe we can say that when an entity is removed, what happens entirely depends on the
|
I have found
EntityPersister cleans up m2m join-table rows with a DELETE statement when an entity is removed, unless onDeleteCascade is set. It works at the SQL level and can remove all join table rows referring to that particular entity in a single query.
That code only works when the association is declared on the side of the entity being removed, either as the owning or inverse side. That is not the case for the
→ Need to check if that causes an amount of extra queries. |
Without the change from #10486, the tests in #10753 pass... unless you make the association bidirectional. Note that the database-level foreign keys are still at their default, diff --git a/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php b/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php
index e508e06a86..b154ab8ef5 100644
--- a/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php
+++ b/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php
@@ -88,7 +88,7 @@ class GH10752Order
private $id = null;
/**
- * @ORM\ManyToMany(targetEntity="GH10752Promotion", cascade={"persist"})
+ * @ORM\ManyToMany(targetEntity="GH10752Promotion", inversedBy="orders", cascade={"persist"})
* @ORM\JoinTable(name="order_promotion",
* joinColumns={@ORM\JoinColumn(name="order_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={@ORM\JoinColumn(name="promotion_id", referencedColumnName="id")}
@@ -142,6 +142,13 @@ class GH10752Promotion
*/
private $id = null;
+ /**
+ * @ORM\ManyToMany(targetEntity="GH10752Order", mappedBy="promotions")
+ *
+ * @var Collection
+ */
+ private $orders;
+
public function getId(): ?int
{
return $this->id; |
It's not that weird IMO. If |
#10486 had another side effect: We started to take care of the dirty collections (where to-be-removed entities have been removed), which happens before we reach I'll add query count expectations to |
A possible fix including documentation updates is in #10763. |
Bug Report
Summary
Since the following change

Doctrine stopped throwing
ForeignKeyConstraintViolationException
and (from what I noticed) it removes the entity from the database. What's more interesting, if we add the deleted lines, and leave the same code at the end of the method – it doesn't work. So putting thisif
statement somehow breaks the expected behavior.Current behavior
No
ForeignKeyConstraintViolationException
🤷🏼♂️.How to reproduce
#10753
Expected behavior
ForeignKeyConstraintViolationException
should be thrown.The text was updated successfully, but these errors were encountered: