DDC-742: Can't add entities to a collection that already has elements that were persisted #914

Closed
opened 2026-01-22 12:55:19 +01:00 by admin · 22 comments
Owner

Originally created by @doctrinebot on GitHub (Aug 11, 2010).

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user kanundrum:

On appending an element to a collection with existing elements, integrity constraint errors are being thrown because it's trying to save the elements in the collection that were already there along with the new elements. Saving the first element is fine and works as expected it's the subsequent additions that give problems. It's a uni-directional association very similar to what is shown in the example on http://www.doctrine-project.org/projects/orm/2.0/docs/reference/working-with-associations/en#establishing-associations.

Originally created by @doctrinebot on GitHub (Aug 11, 2010). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user kanundrum: On appending an element to a collection with existing elements, integrity constraint errors are being thrown because it's trying to save the elements in the collection that were already there along with the new elements. Saving the first element is fine and works as expected it's the subsequent additions that give problems. It's a uni-directional association very similar to what is shown in the example on http://www.doctrine-project.org/projects/orm/2.0/docs/reference/working-with-associations/en#establishing-associations.
admin added the Bug label 2026-01-22 12:55:19 +01:00
admin closed this issue 2026-01-22 12:55:20 +01:00
Author
Owner

@doctrinebot commented on GitHub (Aug 11, 2010):

Comment created by romanb:

And what/where is the bug? What you described is expected behavior. A collection is semantically the same as a php array, so an ordered map, maps allow duplicates. Obviously if the database schema does not you get a constraint violation. Nothing unexpected here.

@doctrinebot commented on GitHub (Aug 11, 2010): Comment created by romanb: And what/where is the bug? What you described is expected behavior. A collection is semantically the same as a php array, so an ordered map, maps allow duplicates. Obviously if the database schema does not you get a constraint violation. Nothing unexpected here.
Author
Owner

@doctrinebot commented on GitHub (Aug 11, 2010):

Comment created by kanundrum:

Ah ok. So using the user favorite comment example. If I have a user and that user has a list of existing comments he's marked as favorite and saved in the db. I hoped that adding a new favorite comment meant adding the new comment to the "comments" collection on the user entity and then flushing the manager and only the new elements I just added would be persisted. If this isn't the case then adding to a collection and persisting a collection only works the first time (when there were no associations) and for future updates I should not be using doctrine?

@doctrinebot commented on GitHub (Aug 11, 2010): Comment created by kanundrum: Ah ok. So using the user favorite comment example. If I have a user and that user has a list of existing comments he's marked as favorite and saved in the db. I hoped that adding a new favorite comment meant adding the new comment to the "comments" collection on the user entity and then flushing the manager and only the new elements I just added would be persisted. If this isn't the case then adding to a collection and persisting a collection only works the first time (when there were no associations) and for future updates I should not be using doctrine?
Author
Owner

@doctrinebot commented on GitHub (Aug 11, 2010):

Comment created by romanb:

I don't think I understand what you're saying.

It is as simple as this: A collection is a map that allows duplicate values (not keys) just like a PHP array. If you want to prevent duplicate entries before they hit the database you need to check whether the collection/array contains an element already before adding it. That is all.

@doctrinebot commented on GitHub (Aug 11, 2010): Comment created by romanb: I don't think I understand what you're saying. It is as simple as this: A collection is a **map** that allows duplicate values (not keys) just like a PHP array. If you want to prevent duplicate entries before they hit the database you need to check whether the collection/array contains an element already before adding it. That is all.
Author
Owner

@doctrinebot commented on GitHub (Aug 11, 2010):

Comment created by kanundrum:

Ok so the collection state is just a map just like a PHP array (I think i got that :) ), it doesn't have any state tracking the way an entity does. i.e. when i retrieve a user entity if I make an update to it and persist it doesn't try to make a new object it updates the entity, but with collections let's say i retrieve my user entity (fetch is set to EAGER) with a comments collection and the user entity comes back and my comments collection on that entity has 3 comments. I add 1 comment to the collection; because the collection is for all intents and purposes just an array; it's going to try to persist my 3 comments that were there before and the new one. If i have that correct it seems that the documentation on transitive persistence and the bits above (along with examples) are a bit misleading in that it will work as described once (that exact example would work the first time when the user has no favorite comments but any attempt to add other items to the collection will throw errors).

@doctrinebot commented on GitHub (Aug 11, 2010): Comment created by kanundrum: Ok so the collection state is just a map just like a PHP array (I think i got that :) ), it doesn't have any state tracking the way an entity does. i.e. when i retrieve a user entity if I make an update to it and persist it doesn't try to make a new object it updates the entity, but with collections let's say i retrieve my user entity (fetch is set to EAGER) with a comments collection and the user entity comes back and my comments collection on that entity has 3 comments. I add 1 comment to the collection; because the collection is for all intents and purposes just an array; it's going to try to persist my 3 comments that were there before and the new one. If i have that correct it seems that the documentation on transitive persistence and the bits above (along with examples) are a bit misleading in that it will work as described once (that exact example would work the first time when the user has no favorite comments but any attempt to add other items to the collection will throw errors).
Author
Owner

@doctrinebot commented on GitHub (Aug 11, 2010):

Comment created by romanb:

@"... because the collection is for all intents and purposes just an array; it's going to try to persist my 3 comments that were there before and the new one."

No, it should only persist the 1 newly added comment. However there is no check whether it already exists. You can add the same comment twice.

If already persistent collection elements are persisted again even though you only added a new element to the collection then there is something wrong.

@doctrinebot commented on GitHub (Aug 11, 2010): Comment created by romanb: @"... because the collection is for all intents and purposes just an array; it's going to try to persist my 3 comments that were there before and the new one." No, it should only persist the 1 newly added comment. However there is no check whether it already exists. You can add the same comment twice. If already persistent collection elements are persisted **again** even though you only added a new element to the collection then there is something wrong.
Author
Owner

@doctrinebot commented on GitHub (Aug 11, 2010):

Comment created by romanb:

Also, let me clarify (its also in the documentation) that collections only represent the association between entities. Unless you use cascade=persist or remove on a collection-valued association, when you remove or add an element to a collection, it does not mean the element itself is persisted/removed, just the association (which means the join table is updated in the case of many-to-many or one-to-many through jointable or the foreign key in the other cases).

@doctrinebot commented on GitHub (Aug 11, 2010): Comment created by romanb: Also, let me clarify (its also in the documentation) that collections only represent the association between entities. Unless you use cascade=persist or remove on a collection-valued association, when you remove or add an element to a collection, it does not mean the element itself is persisted/removed, just the association (which means the join table is updated in the case of many-to-many or one-to-many through jointable or the foreign key in the other cases).
Author
Owner

@doctrinebot commented on GitHub (Aug 11, 2010):

Comment created by romanb:

Maybe it would be a good idea if you could post some code with example models of what you're doing so we can see whether Doctrine behaves correctly or not. A unit test along the lines of the existing ticket tests would be ideal, of course.

@doctrinebot commented on GitHub (Aug 11, 2010): Comment created by romanb: Maybe it would be a good idea if you could post some code with example models of what you're doing so we can see whether Doctrine behaves correctly or not. A unit test along the lines of the existing ticket tests would be ideal, of course.
Author
Owner

@doctrinebot commented on GitHub (Aug 11, 2010):

Comment created by kanundrum:

Thanks for the explanation and what you outlined is how I thought it would work so i think I'm experiencing a bug. I'll get an example going and accompanying unit tests to verify and I'll update (a bit later today). Thanks for the speedy response.

@doctrinebot commented on GitHub (Aug 11, 2010): Comment created by kanundrum: Thanks for the explanation and what you outlined is how I thought it would work so i think I'm experiencing a bug. I'll get an example going and accompanying unit tests to verify and I'll update (a bit later today). Thanks for the speedy response.
Author
Owner

@doctrinebot commented on GitHub (Aug 11, 2010):

Comment created by kanundrum:

Attached is a standalone copy of the code. To run it put on a webserver and fill in the db config in the index.php(and run the doctrine.sql to setup the database) and put the doctrine library in the vendor folder (apologies for the lack of unit testing)

I am trying to do what I mentioned earlier in the ticket which is append an item to a collection that already had items persisted prior:

$user = $em->getRepository('User')->find(1);
$comment = $em->getRepository('Comment')->find(3);
$user->favoriteComments->add($comment);
$em->flush();
@doctrinebot commented on GitHub (Aug 11, 2010): Comment created by kanundrum: Attached is a standalone copy of the code. To run it put on a webserver and fill in the db config in the index.php(and run the doctrine.sql to setup the database) and put the doctrine library in the vendor folder (apologies for the lack of unit testing) I am trying to do what I mentioned earlier in the ticket which is append an item to a collection that already had items persisted prior: ``` $user = $em->getRepository('User')->find(1); $comment = $em->getRepository('Comment')->find(3); $user->favoriteComments->add($comment); $em->flush(); ```
Author
Owner

@doctrinebot commented on GitHub (Aug 15, 2010):

Comment created by @beberlei:

Verified, this has to do with fetch="EAGER" on the many side.

@doctrinebot commented on GitHub (Aug 15, 2010): Comment created by @beberlei: Verified, this has to do with fetch="EAGER" on the many side.
Author
Owner

@doctrinebot commented on GitHub (Aug 15, 2010):

Comment created by @beberlei:

Fixed in master

@doctrinebot commented on GitHub (Aug 15, 2010): Comment created by @beberlei: Fixed in master
Author
Owner

@doctrinebot commented on GitHub (Aug 15, 2010):

Comment created by @beberlei:

Btw akeem, setting a collection to EAGER is very bad practice. It execute an additional sql call to hydrate those entries and will become a performance bottleneck once users have many favorites. Additionally it loads the favorite comments ALWAYS, even in requests where you dont need them.

@doctrinebot commented on GitHub (Aug 15, 2010): Comment created by @beberlei: Btw akeem, setting a collection to EAGER is very bad practice. It execute an additional sql call to hydrate those entries and will become a performance bottleneck once users have many favorites. Additionally it loads the favorite comments ALWAYS, even in requests where you dont need them.
Author
Owner

@doctrinebot commented on GitHub (Sep 16, 2010):

Comment created by kanundrum:

Now I am experiencing same symptoms when the cache driver is set to \Doctrine\Common\Cache\MemcacheCache. (the same example code applies just change to the adapter to Memcache)

@doctrinebot commented on GitHub (Sep 16, 2010): Comment created by kanundrum: Now I am experiencing same symptoms when the cache driver is set to \Doctrine\Common\Cache\MemcacheCache. (the same example code applies just change to the adapter to Memcache)
Author
Owner

@doctrinebot commented on GitHub (Sep 17, 2010):

Comment created by @beberlei:

@akeem what was the adapter before? ArrayCache?

@doctrinebot commented on GitHub (Sep 17, 2010): Comment created by @beberlei: @akeem what was the adapter before? ArrayCache?
Author
Owner

@doctrinebot commented on GitHub (Sep 17, 2010):

Comment created by kanundrum:

Yes.

@doctrinebot commented on GitHub (Sep 17, 2010): Comment created by kanundrum: Yes.
Author
Owner

@doctrinebot commented on GitHub (Sep 22, 2010):

Comment created by @beberlei:

I modified the test to use memcache or APC and re-grab serialized content from the cache for this test, however nothing changes.

Can you modify the tests/Doctrine/Tests/ORM/Functional/Ticket/DDC742Test.php to get the failure?

you can run the testsuite from the tests/ directory calling "phpunit --filter DDC742 Doctrine/Tests/AllTests.php"

@doctrinebot commented on GitHub (Sep 22, 2010): Comment created by @beberlei: I modified the test to use memcache or APC and re-grab serialized content from the cache for this test, however nothing changes. Can you modify the tests/Doctrine/Tests/ORM/Functional/Ticket/DDC742Test.php to get the failure? you can run the testsuite from the tests/ directory calling "phpunit --filter DDC742 Doctrine/Tests/AllTests.php"
Author
Owner

@doctrinebot commented on GitHub (Sep 27, 2010):

Comment created by kanundrum:

Yeah I tried to change things up in the test myself and didn't have much luck replicating. I put the code in the test in my ad hoc test and at first it looked fine until i got rid of setup and tear down code, deleted the mapping between the user and the third comment manually in the database and then ran the adhoc just to add the third comment and I got the error again.

Integrity constraint violation: 1062 Duplicate entry '1-1' for key 'PRIMARY' in /var/www/doctrinetest/vendor/Doctrine/DBAL/Connection.php

which suggests that it's trying to re-add the first comment

I'm not sure if it is that in the test the find result is not really coming from memcached (is there a way to test this in the unit test?). I'll attach my modified DDC742Test.php so you can see the differences. Most notable I tried breaking it out into different steps (and hacking up the setup function in the process :) ), switched to using mysql db (didn't want any differences) and changed the memcache setting block to this (not sure if the setQueryCacheImpl was necessary or not:

$cache = new \Doctrine\Common\Cache\MemcacheCache();
        $memcached = new \Memcache();
                $memcached->connect('127.0.0.1', 11211);
        $cache->setMemcache($memcached);

            $this->_em->getMetadataFactory()->setCacheDriver($cache);
        $this->_em->getConfiguration()->setQueryCacheImpl($cache);
@doctrinebot commented on GitHub (Sep 27, 2010): Comment created by kanundrum: Yeah I tried to change things up in the test myself and didn't have much luck replicating. I put the code in the test in my ad hoc test and at first it looked fine until i got rid of setup and tear down code, deleted the mapping between the user and the third comment manually in the database and then ran the adhoc just to add the third comment and I got the error again. ``` Integrity constraint violation: 1062 Duplicate entry '1-1' for key 'PRIMARY' in /var/www/doctrinetest/vendor/Doctrine/DBAL/Connection.php ``` which suggests that it's trying to re-add the first comment I'm not sure if it is that in the test the find result is not really coming from memcached (is there a way to test this in the unit test?). I'll attach my modified DDC742Test.php so you can see the differences. Most notable I tried breaking it out into different steps (and hacking up the setup function in the process :) ), switched to using mysql db (didn't want any differences) and changed the memcache setting block to this (not sure if the setQueryCacheImpl was necessary or not: ``` $cache = new \Doctrine\Common\Cache\MemcacheCache(); $memcached = new \Memcache(); $memcached->connect('127.0.0.1', 11211); $cache->setMemcache($memcached); $this->_em->getMetadataFactory()->setCacheDriver($cache); $this->_em->getConfiguration()->setQueryCacheImpl($cache); ```
Author
Owner

@doctrinebot commented on GitHub (Sep 27, 2010):

Comment created by kanundrum:

My modified version of the testcase

@doctrinebot commented on GitHub (Sep 27, 2010): Comment created by kanundrum: My modified version of the testcase
Author
Owner

@doctrinebot commented on GitHub (Oct 30, 2010):

Comment created by @beberlei:

still works for me

@doctrinebot commented on GitHub (Oct 30, 2010): Comment created by @beberlei: still works for me
Author
Owner

@doctrinebot commented on GitHub (Jul 31, 2011):

Comment created by @beberlei:

No feedback was given on this issue, closing. Please open a new issue if this is still a problem.

@doctrinebot commented on GitHub (Jul 31, 2011): Comment created by @beberlei: No feedback was given on this issue, closing. Please open a new issue if this is still a problem.
Author
Owner

@doctrinebot commented on GitHub (Jul 31, 2011):

Issue was closed with resolution "Invalid"

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

@doctrinebot commented on GitHub (Dec 13, 2015):

Imported 1 attachments from Jira into https://gist.github.com/39aa6adaee76d8d115ed

@doctrinebot commented on GitHub (Dec 13, 2015): Imported 1 attachments from Jira into https://gist.github.com/39aa6adaee76d8d115ed - [10815_DDC742Test.php](https://gist.github.com/39aa6adaee76d8d115ed#file-10815_DDC742Test-php)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#914