DDC-767: Updating many-to-many relations #948

Closed
opened 2026-01-22 12:56:51 +01:00 by admin · 12 comments
Owner

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

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user ambis:

I have an entity with a many-to-many relation.

When I delete all relations and then flush, add new relations and then flush, all inside one transaction, I get

Fatal error: Call to a member function update() on a non-object in Doctrine/ORM/UnitOfWork.php on line 312

        // Delete old categories
        foreach ($revision->getCategories() as $category) {
            $revision->removeCategory($category);
        }

        $this->entityManager->flush();

        // Add new
        foreach ($categories as $categoryId) {
            $category = $this->entityManager->find('Category', $categoryId);

            if ($category instanceof Category) {
                $revision->addCategory($category);
            }
        }

        $this->entityManager->flush();
    /****
     * @return ArrayCollection
     */
    public function getCategories()
    {
        return $this->categories;
    }
    /****
     * @param Category $category
     * @return Revision
     */
    public function addCategory(Category $category)
    {
        $this->categories->add($category);
        return $this;
    }
    /****
     * @param Category $category
     * @return Revision
     */
    public function removeCategory(Category $category)
    {
        $this->categories->removeElement($category);
        return $this;
    }

If this is not a bug, I'd like to know which is the most efficient and elegant way to update many-to-many relationships.

Originally created by @doctrinebot on GitHub (Aug 25, 2010). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user ambis: I have an entity with a many-to-many relation. When I delete all relations and then flush, add new relations and then flush, all inside one transaction, I get Fatal error: Call to a member function update() on a non-object in Doctrine/ORM/UnitOfWork.php on line 312 ``` // Delete old categories foreach ($revision->getCategories() as $category) { $revision->removeCategory($category); } $this->entityManager->flush(); // Add new foreach ($categories as $categoryId) { $category = $this->entityManager->find('Category', $categoryId); if ($category instanceof Category) { $revision->addCategory($category); } } $this->entityManager->flush(); ``` ``` /**** * @return ArrayCollection */ public function getCategories() { return $this->categories; } ``` ``` /**** * @param Category $category * @return Revision */ public function addCategory(Category $category) { $this->categories->add($category); return $this; } ``` ``` /**** * @param Category $category * @return Revision */ public function removeCategory(Category $category) { $this->categories->removeElement($category); return $this; } ``` If this is not a bug, I'd like to know which is the most efficient and elegant way to update many-to-many relationships.
admin added the Bug label 2026-01-22 12:56:51 +01:00
admin closed this issue 2026-01-22 12:56:51 +01:00
Author
Owner

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

@doctrinebot commented on GitHub (Aug 25, 2010): - is duplicated by [DDC-839: Error when trying to update PersistentCollection](http://www.doctrine-project.org/jira/browse/DDC-839)
Author
Owner

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

Comment created by ambis:

When I add at least one relation between Revision and Category (directly into the db) everything works just as expected. I can add more and delete old. But as soon as all relations are deleted, this problem occurs.

@doctrinebot commented on GitHub (Aug 25, 2010): Comment created by ambis: When I add at least one relation between Revision and Category (directly into the db) everything works just as expected. I can add more and delete old. But as soon as all relations are deleted, this problem occurs.
Author
Owner

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

Comment created by ambis:

I changed Revision::removeCategory() to Revision::removeCategories() which calls ->clear() on the ArrayColleciton. This didn't have any effect.

I also created a WHERE IN-query for the new categories to optimize the code a bit.

@doctrinebot commented on GitHub (Aug 25, 2010): Comment created by ambis: I changed Revision::removeCategory() to Revision::removeCategories() which calls ->clear() on the ArrayColleciton. This didn't have any effect. I also created a WHERE IN-query for the new categories to optimize the code a bit.
Author
Owner

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

Comment created by ambis:

After updating to BETA4, the error message has changed:

Catchable fatal error: Argument 1 passed to Doctrine\ORM\UnitOfWork::getCollectionPersister() must be an array, null given, called in /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php on line 297 and defined in /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php on line 2131

    /****
     * @param Revision $revision
     * @param array $categories
     * @return Revision
     */
    private function updateRevisionCategories(Revision $revision, array $categories)
    {
        // Delete old categories
        $revision->removeCategories();
        $this->entityManager->flush();

        if ( ! empty($categories)) {
            // Query and add new ones
            $qb = $this->entityManager->createQueryBuilder();

            $qb->select('category')
               ->from('Category', 'category')
               ->where($qb->expr()->in('category.id', $categories));

            $query = $this->entityManager->createQuery($qb);

            foreach ($query->getResult() as $category) {
                $revision->addCategory($category);
            }

            $this->entityManager->flush();
        }

        return $revision;
    }
@doctrinebot commented on GitHub (Sep 2, 2010): Comment created by ambis: After updating to BETA4, the error message has changed: Catchable fatal error: Argument 1 passed to Doctrine\ORM\UnitOfWork::getCollectionPersister() must be an array, null given, called in /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php on line 297 and defined in /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php on line 2131 ``` /**** * @param Revision $revision * @param array $categories * @return Revision */ private function updateRevisionCategories(Revision $revision, array $categories) { // Delete old categories $revision->removeCategories(); $this->entityManager->flush(); if ( ! empty($categories)) { // Query and add new ones $qb = $this->entityManager->createQueryBuilder(); $qb->select('category') ->from('Category', 'category') ->where($qb->expr()->in('category.id', $categories)); $query = $this->entityManager->createQuery($qb); foreach ($query->getResult() as $category) { $revision->addCategory($category); } $this->entityManager->flush(); } return $revision; } ```
Author
Owner

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

Comment created by @beberlei:

I committed a testcase with your scenario that shows it works. It seems there is something wrong with your entity code, can you show more? Or try to make tests/Doctrine/Tests/ORM/Functional/Ticket/DDC767Test.php

810a129a32

@doctrinebot commented on GitHub (Sep 15, 2010): Comment created by @beberlei: I committed a testcase with your scenario that shows it works. It seems there is something wrong with your entity code, can you show more? Or try to make tests/Doctrine/Tests/ORM/Functional/Ticket/DDC767Test.php http://github.com/doctrine/doctrine2/commit/810a129a3273a3826ecedb4b744a55e33a54a3ff
Author
Owner

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

Comment created by ambis:

Yeah I just now noticed you were using em->clear() there.

I got all things working after I started using:

entity->removeCategories()

em->flush()
em->clear()

entity = em->merge(entity)

Is ther something inherintly wrong here? I wouldn't want to reload the entity because 1) I would neet to hit the db and 2) all the relations might not come along which are already loaded for it when first obtained before.

@doctrinebot commented on GitHub (Oct 2, 2010): Comment created by ambis: Yeah I just now noticed you were using em->clear() there. I got all things working after I started using: entity->removeCategories() em->flush() em->clear() entity = em->merge(entity) Is ther something inherintly wrong here? I wouldn't want to reload the entity because 1) I would neet to hit the db and 2) all the relations might not come along which are already loaded for it when first obtained before.
Author
Owner

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

Comment created by ambis:

All was good up until I realized that using the em->merge(entity) breaks the object reference.

I do things much like:

$entity = loadById($id) beginTransaction() try { service->doStuff($entity, $data) otherService->doOtherStuff($entity, $data) commit() } catch ...

Now, when I have to merge the entity inside the doStuff() service method, the managed object is not the same object doOtherStuff gets.

So, what I've gathered is that a PersistentCollection cannot be cleared, flushed, re-populated and then flushed again without things breaking catastrophically (see my 3rd comment for the error I get).

I'd like to see your test case work without clearing the entity manager at any point.

Or at least I'd love to hear the reasoning behind the clearing process if it truly is required.

@doctrinebot commented on GitHub (Oct 2, 2010): Comment created by ambis: All was good up until I realized that using the em->merge(entity) breaks the object reference. I do things much like: `$entity = loadById($id) beginTransaction() try { service->doStuff($entity, $data) otherService->doOtherStuff($entity, $data) commit() } catch ...` Now, when I have to merge the entity inside the doStuff() service method, the managed object is not the same object doOtherStuff gets. So, what I've gathered is that a PersistentCollection cannot be cleared, flushed, re-populated and then flushed again without things breaking catastrophically (see my 3rd comment for the error I get). I'd like to see your test case work without clearing the entity manager at any point. Or at least I'd love to hear the reasoning behind the clearing process if it truly is required.
Author
Owner

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

Comment created by ambis:

Here is a test which fails with the following error and is 1:1 to my case:

Argument 1 passed to Doctrine\ORM\UnitOfWork::getCollectionPersister() must be an array, null given, called in doctrine2/lib/Doctrine/ORM/UnitOfWork.php on line 302 and defined

Full trace in my app:

exception 'ErrorException' with message 'Argument 1 passed to Doctrine\ORM\UnitOfWork::getCollectionPersister() must be an array, null given, called in /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php on line 302 and defined' in /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php:2131 Stack trace: #0 /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php(2131): *****_ErrorHandler::handleError(4096, 'Argument 1 pass...', '/usr/local/zend...', 2131, Array) #1 /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php(302): Doctrine\ORM\UnitOfWork->getCollectionPersister(NULL) #2 /usr/local/zend/share/pear/Doctrine/ORM/EntityManager.php(320): Doctrine\ORM\UnitOfWork->commit() #3 *****/ImageGroupService.php(182): Doctrine\ORM\EntityManager->flush() #4 ****EntryUsercpController.php(104): ***ImageGroupService->setImageGroupImages(Object(****ImageGroup), Array, NULL) #5 library/ZendFramework-1.10.8/library/Zend/Controller/Action.php(513): *****EntryUsercpController->imagesAction() #6 library/ZendFramework-1.10.8/library/Zend/Controller/Dispatcher/Standard.php(295): Zend*Controller*Action->dispatch('imagesAction') #7 library/ZendFramework-1.10.8/library/Zend/Controller/Front.php(954): Zend*Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response*Http)) #8 library/ZendFramework-1.10.8/library/Zend/Application/Bootstrap/Bootstrap.php(97): Zend*Controller*Front->dispatch() #9 library/ZendFramework-1.10.8/library/Zend/Application.php(366): Zend*Application_Bootstrap*Bootstrap->run() #10 dev.sotavasara.net/published/index.php(26): Zend_Application->run() #11 {main}

@doctrinebot commented on GitHub (Oct 2, 2010): Comment created by ambis: Here is a test which fails with the following error and is 1:1 to my case: `Argument 1 passed to Doctrine\ORM\UnitOfWork::getCollectionPersister() must be an array, null given, called in doctrine2/lib/Doctrine/ORM/UnitOfWork.php on line 302 and defined` Full trace in my app: `exception 'ErrorException' with message 'Argument 1 passed to Doctrine\ORM\UnitOfWork::getCollectionPersister() must be an array, null given, called in /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php on line 302 and defined' in /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php:2131 Stack trace: #0 /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php(2131): *****_ErrorHandler::handleError(4096, 'Argument 1 pass...', '/usr/local/zend...', 2131, Array) #1 /usr/local/zend/share/pear/Doctrine/ORM/UnitOfWork.php(302): Doctrine\ORM\UnitOfWork->getCollectionPersister(NULL) #2 /usr/local/zend/share/pear/Doctrine/ORM/EntityManager.php(320): Doctrine\ORM\UnitOfWork->commit() #3 *****/ImageGroupService.php(182): Doctrine\ORM\EntityManager->flush() #4 ****EntryUsercpController.php(104): ***ImageGroupService->setImageGroupImages(Object(****ImageGroup), Array, NULL) #5 library/ZendFramework-1.10.8/library/Zend/Controller/Action.php(513): *****EntryUsercpController->imagesAction() #6 library/ZendFramework-1.10.8/library/Zend/Controller/Dispatcher/Standard.php(295): Zend*Controller*Action->dispatch('imagesAction') #7 library/ZendFramework-1.10.8/library/Zend/Controller/Front.php(954): Zend*Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response*Http)) #8 library/ZendFramework-1.10.8/library/Zend/Application/Bootstrap/Bootstrap.php(97): Zend*Controller*Front->dispatch() #9 library/ZendFramework-1.10.8/library/Zend/Application.php(366): Zend*Application_Bootstrap*Bootstrap->run() #10 dev.sotavasara.net/published/index.php(26): Zend_Application->run() #11 {main}`
Author
Owner

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

Comment created by ambis:

Please review attached test case.

@doctrinebot commented on GitHub (Oct 2, 2010): Comment created by ambis: Please review attached test case.
Author
Owner

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

Comment created by @beberlei:

Duplicate of DDC-839, and fixed.

@doctrinebot commented on GitHub (Oct 30, 2010): Comment created by @beberlei: Duplicate of [DDC-839](http://www.doctrine-project.org/jira/browse/DDC-839), and fixed.
Author
Owner

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

Issue was closed with resolution "Duplicate"

@doctrinebot commented on GitHub (Oct 30, 2010): Issue was closed with resolution "Duplicate"
Author
Owner

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

Imported 1 attachments from Jira into https://gist.github.com/882d50413e4fb0131491

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

No dependencies set.

Reference: doctrine/archived-orm#948