mirror of
https://github.com/doctrine/orm.git
synced 2026-03-23 22:42:18 +01:00
DDC-956: PersistentCollection::clear does not clear the collection #1194
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 @doctrinebot on GitHub (Dec 29, 2010).
Originally assigned to: @beberlei on GitHub.
Jira issue originally created by user flynsarmy:
The above code uses one (User) to many (Tokens) entities. $User->getTokens()->count() changes to 0 after the clear but the tokens are not removed from the DB upon flush. Also tried adding a Doctrine::em()->persist( $User ) above the flush() line.
This behaviour was the same whether or not orphanRemoval=true was present in annotations in the User entity class:
In addition, there's no orphanRemoval option in the YAML mapping driver. Haven't looked at the XML one.
@doctrinebot commented on GitHub (Dec 29, 2010):
@doctrinebot commented on GitHub (Dec 30, 2010):
Comment created by @beberlei:
Your mapping is wrong, you are using "mappedBy" twice. The owning side has to use "inversedBy".
Edit: Oh wait this is the same mapping twice. Can you post your other side?
@doctrinebot commented on GitHub (Dec 30, 2010):
Comment created by @beberlei:
This is not a bug, you are clearing the collection of the inverse side. However changes to the inverse side of a bi-directional association are not saved into the database.
In this case i can see this seems rather annoying, what would you expect to happen? Set all the ManyToOne side columns to NULL?
@doctrinebot commented on GitHub (Dec 30, 2010):
Comment created by flynsarmy:
I would there to be some simple command to delete all of the Many side from the One side. EG a User table and UserLog table - how do you easily delete all the user logs? You could use DQL then refresh the User object if it's already loaded but that's very longwinded. Shouldn't there be a User->getUserLogs()->removeAll() or something? That's what I expect clear() to do. At the moment clear() is no more useful than $em->remove( $UserLog->getUser() ); You would think clear would be PERFECTLY suited to the above scenario.
@doctrinebot commented on GitHub (Feb 18, 2011):
Comment created by rrafal@gmail.com:
I'm experiencing a similar issue. I'm sure there's a bug.
I'm using collection \Doctrine\Common\Collections\ArrayCollection inside an entity. If I call clear(), the collection is not cleared:
$list->clear();
$list->count(); // returns 7
However, if I access the collection first and then clear it, the collection is actually cleared.
$list->first();
$list->clear();
$list->count(); // returns 0
I'm guessing this is related to lazy loading of collection.
Rafal
@doctrinebot commented on GitHub (May 24, 2011):
Comment created by darkangel:
I have the same issue ... I don't see why $tournament->getTournamentSponsors()->clear(); should have no affect on the database.
@doctrinebot commented on GitHub (May 24, 2011):
Comment created by @beberlei:
because its the inverse side of the collection.
@doctrinebot commented on GitHub (May 24, 2011):
Comment created by darkangel:
I realize that it's the inverse side of the collection, but it's the only logical side of the association from which to remove elements. What is the reason for the restriction?
Do I have to use DQL to delete all tournament sponsor records for a particular tournament? Or something like:
... which will delete each sponsor separately.
What I'm actually trying to do is this: http://stackoverflow.com/questions/6107996/how-to-synchronize-a-doctrine-2-collection
Since I couldn't figure out how to do that (can you help?), I thought of just deleting all sponsors, and creating them again (which is far from ideal, since you end up increasing the id values every time you update a tournament's list of sponsors).
Is there some way to construct a collection of sponsors, and merge/sync it with the current collection attached to a tournament, so that new sponsors are added, missing ones are removed from the database, and existing ones remain unaffected?
@doctrinebot commented on GitHub (May 24, 2011):
Comment created by flynsarmy:
I would like a decent reason as to why you can't do $User->getUserLogs()->clear() too (When 1 User has many Logs)...it makes so much sense. I don't particularly care if it's 'the inverse side of the colleciton'...it should do what it says it is doing. How DO you clear all the logs for a given user short of a completely separate DQL statement then reloading the User object? Does D2 provide any easy way of doing something so simple?
@doctrinebot commented on GitHub (May 26, 2011):
Comment created by danielh:
I haven't tried it out, but is orphan removal what you're looking for?
http://www.doctrine-project.org/docs/orm/2.0/en/reference/working-with-associations.html#orphan-removal
From that same page,
8.3. Removing Associations
8.4. Association Management Methods
8.5. Synchronizing Bidirectional Collections
May also be of interest for your particular problem.
@doctrinebot commented on GitHub (Jun 19, 2011):
Comment created by johnnyp:
@doctrinebot commented on GitHub (Jun 19, 2011):
Comment created by flynsarmy:
@Johnny Peck
Calm down, buddy. Try to contain your ignorance and high-horse attitude
I was merely pointing out the fact that you can use clear() and you won't get an error (from memory) - it just won't do anything. If this is indeed the case then it's pretty bad design at best, an error at worst.
@doctrinebot commented on GitHub (Jun 19, 2011):
Comment created by @beberlei:
Calling Collection::clear() on the inverse side will not be supported, its causing tons of problems that can't be fixed.
For example Post has Many comments
If you would do $post->comments->clear() all the Comment instances in memory will still have Comment::$post set. And if you flush an entity the post_id might be saved there again, although you cleared the collection. There is just no way to support this without edge cases causing problems.
I verified Hibernate works the same and does not throw an exception on inverse side Collection::clear call.
Workaround is to do:
$post->comments->forAll(function ($comment) { $comment->post = null; });
I don't want to do such a thing internally, because it would be rather magic and can cause tons of performance problems (initializes the collection).
See DDC-1189 for a related issue that was fixed just now.
@doctrinebot commented on GitHub (Jun 19, 2011):
Issue was closed with resolution "Invalid"
@doctrinebot commented on GitHub (Sep 7, 2011):
Comment created by steven:
I'm sorry to drag this discussion back up again, but I wanted to clarify something.
Benjamin, you're claiming that calling Collection::clear() on the inverse side would cause a bunch of problems, yet we can simply remove an element from the inverse side without modifying the owning side. Although this flies in the face of the Doctrine documentation claiming all modifications to a relationship must happen on the owning side, the documentation for "orphanRemoval" contradicts this and allows us to sort of control it from the inverse side.
For example:
Strangely, both seem to have similar functionality - an element is removed from the _elements array inside the ArrayCollection class. Perhaps I'm missing something.
If I iterate through the collection and remove each element explicitly (rather than simply clearing the collection property), it removes the data from the database when persisting entity A. I thought this was the entire point of the orphanRemoval attribute. I was hoping this functionality would also be present when clearing the collection.
Do I understand correctly that this simply isn't implemented due to the headaches of clearing an uninitialised collection (one that hasn't yet been loaded from the database)?
Kind regards
[edit]
Sorry, I just realised, of course, performing ArrayCollection::clear() doesn't work off the bat like removeElement() because the collection hasn't been initialised (loaded from the database yet). That makes sense and you're saying that initialising a collection just to perform clear() on it is/will cause other problems? The quick hack (if using orphanRemoval) is simply to initialise the collection by calling the ->count() method, then clearing it, but that seems a little dirty.
@doctrinebot commented on GitHub (Sep 7, 2011):
Comment created by rrafal@gmail.com:
I want to say that I strongly agree with Steven Bakhtiari: "The quick hack (if using orphanRemoval) is simply to initialise the collection by calling the ->count() method, then clearing it, but that seems a little dirty."
I believe that this is a bug in Doctrine because the result of calling clear() is not consistent. You argue that clearing the inverse side is not supported. Then, this operation should never be supported. It should always throw an exception. This is NOT a bug because clearing inverse size of collection is unsupported. This is a bug because clearing inverse size of collection sometimes works and sometimes doesn't.
When I run into this problem, there was a bug in my application. A collection failed to be cleared. However, when I was testing, everything worked correctly because I happened to check size of collection before clearing it.
Any new opinions?
-Rafal
@bkrnetic commented on GitHub (Aug 1, 2022):
I agree with everything mentioned before. The workaround for this is setting an empty collection and then saving the owning entity. See the example below:
I am not happy with the solution but at least it is the least dirty one.
@ceadreak commented on GitHub (Feb 10, 2023):
@bkrnetic it's a quite ugly, so you have to save entity two times if you want to reset a collection and then add new object inside ? One time just after "clear", second time to save changes
@bkrnetic commented on GitHub (Feb 10, 2023):
No, only once, so the code would be:
Give it a try!