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

Closed
doctrinebot opened this Issue Aug 25, 2010 · 12 comments

2 participants

@doctrinebot

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.

@doctrinebot

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

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

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

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

@doctrinebot

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

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

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

Comment created by ambis:

Please review attached test case.

@doctrinebot

Comment created by @beberlei:

Duplicate of DDC-839, and fixed.

@doctrinebot

Issue was closed with resolution "Duplicate"

@beberlei beberlei was assigned by doctrinebot Dec 6, 2015
@doctrinebot doctrinebot added this to the 2.0-RC1 milestone Dec 6, 2015
@doctrinebot doctrinebot closed this Dec 6, 2015
@doctrinebot doctrinebot added the Bug label Dec 7, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment