DDC-956: PersistentCollection::clear does not clear the collection #1194

Closed
opened 2026-01-22 13:05:36 +01:00 by admin · 19 comments
Owner

Originally created by @doctrinebot on GitHub (Dec 29, 2010).

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user flynsarmy:

$User = Doctrine::em()->find('models\User', 1);
echo $User->getTokens()->count(); //prints 7
$User->getTokens()->clear();
echo $User->getTokens()->count(); //prints 0

Doctrine::em()->flush();

//By this point, the tokens are not removed from the DB

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:

  /****
     * @var models\UserToken
     *
     * @OneToMany(targetEntity="models\UserToken", mappedBy="user", orphanRemoval=true)
     */
    private $tokens;

//or

  /****
     * @var models\UserToken
     *
     * @OneToMany(targetEntity="models\UserToken", mappedBy="user")
     */
    private $tokens;

In addition, there's no orphanRemoval option in the YAML mapping driver. Haven't looked at the XML one.

Originally created by @doctrinebot on GitHub (Dec 29, 2010). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user flynsarmy: ``` $User = Doctrine::em()->find('models\User', 1); echo $User->getTokens()->count(); //prints 7 $User->getTokens()->clear(); echo $User->getTokens()->count(); //prints 0 Doctrine::em()->flush(); //By this point, the tokens are not removed from the DB ``` 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: ``` /**** * @var models\UserToken * * @OneToMany(targetEntity="models\UserToken", mappedBy="user", orphanRemoval=true) */ private $tokens; //or /**** * @var models\UserToken * * @OneToMany(targetEntity="models\UserToken", mappedBy="user") */ private $tokens; ``` In addition, there's no orphanRemoval option in the YAML mapping driver. Haven't looked at the XML one.
admin added the Bug label 2026-01-22 13:05:36 +01:00
admin closed this issue 2026-01-22 13:05:36 +01:00
Author
Owner

@doctrinebot commented on GitHub (Dec 29, 2010):

@doctrinebot commented on GitHub (Dec 29, 2010): - is referenced by [DDC-1189: PersistentCollection::clear() does not cause the collection to be initialized](http://www.doctrine-project.org/jira/browse/DDC-1189)
Author
Owner

@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: 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?
Author
Owner

@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 @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?
Author
Owner

@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 (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.
Author
Owner

@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 (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
Author
Owner

@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 darkangel: I have the same issue ... I don't see why $tournament->getTournamentSponsors()->clear(); should have no affect on the database.
Author
Owner

@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 @beberlei: because its the inverse side of the collection.
Author
Owner

@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:

foreach ($tournament->getTournamentSponsors() as $sponsor) {
    $em->remove($sponsor);
}

... 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 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: ``` foreach ($tournament->getTournamentSponsors() as $sponsor) { $em->remove($sponsor); } ``` ... 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?
Author
Owner

@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 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?
Author
Owner

@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 (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.
Author
Owner

@doctrinebot commented on GitHub (Jun 19, 2011):

Comment created by johnnyp:

@doctrinebot commented on GitHub (Jun 19, 2011): Comment created by johnnyp:
Author
Owner

@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 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.
Author
Owner

@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): 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](http://www.doctrine-project.org/jira/browse/DDC-1189) for a related issue that was fixed just now.
Author
Owner

@doctrinebot commented on GitHub (Jun 19, 2011):

Issue was closed with resolution "Invalid"

@doctrinebot commented on GitHub (Jun 19, 2011): Issue was closed with resolution "Invalid"
Author
Owner

@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:

class A
{
    /****
     * @OneToMany(targetEntity="B", mappedBy="a", orphanRemoval=true)
     */
    protected $bCollection;

    // snip

    public function removeBs()
    {
        // the following seemingly works
        foreach ($this->bCollection as $b) {
            $this->bCollection->removeElement($b);
        }

        // however, the following DOES NOT work
        $this->bCollection->clear();
    }
}

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 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: ``` class A { /**** * @OneToMany(targetEntity="B", mappedBy="a", orphanRemoval=true) */ protected $bCollection; // snip public function removeBs() { // the following seemingly works foreach ($this->bCollection as $b) { $this->bCollection->removeElement($b); } // however, the following DOES NOT work $this->bCollection->clear(); } } ``` 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.
Author
Owner

@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

@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
Author
Owner

@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:

$foo->setBars(new ArrayCollection());
Doctrine::em()->persist($foo);
Doctrine::em()->flush();

I am not happy with the solution but at least it is the least dirty one.

@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: ``` $foo->setBars(new ArrayCollection()); Doctrine::em()->persist($foo); Doctrine::em()->flush(); ``` I am not happy with the solution but at least it is the least dirty one.
Author
Owner

@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

@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
Author
Owner

@bkrnetic 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

No, only once, so the code would be:

$foo->setBars(new ArrayCollection());

// add new objects inside

Doctrine::em()->persist($foo);
Doctrine::em()->flush();

Give it a try!

@bkrnetic 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 No, only once, so the code would be: ``` $foo->setBars(new ArrayCollection()); // add new objects inside Doctrine::em()->persist($foo); Doctrine::em()->flush(); ``` Give it a try!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#1194