Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Duplicate
    • Affects Version/s: 2.0-BETA3
    • Fix Version/s: 2.0-RC1
    • Component/s: ORM
    • Security Level: All
    • Labels:
      None

      Description

      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();
      
      Revision::getCategories()
          /**
           * @return ArrayCollection
           */
          public function getCategories()
          {
              return $this->categories;
          }
      
      Revision::addCategory()
          /**
           * @param Category $category
           * @return Revision
           */
          public function addCategory(Category $category)
          {
              $this->categories->add($category);
              return $this;
          }
      
      Revision::removeCategory()
          /**
           * @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.

        Issue Links

          Activity

          Hide
          Matti Niemelä added a comment - - edited

          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.

          Show
          Matti Niemelä added a comment - - edited 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.
          Hide
          Matti Niemelä added a comment -

          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.

          Show
          Matti Niemelä added a comment - 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.
          Hide
          Matti Niemelä added a comment -

          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;
              }
          
          Show
          Matti Niemelä added a comment - 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; }
          Hide
          Benjamin Eberlei added a comment -

          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

          Show
          Benjamin Eberlei added a comment - 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
          Hide
          Matti Niemelä added a comment -

          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.

          Show
          Matti Niemelä added a comment - 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.
          Hide
          Matti Niemelä added a comment - - edited

          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.

          Show
          Matti Niemelä added a comment - - edited 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.
          Hide
          Matti Niemelä added a comment -

          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} 
          Show
          Matti Niemelä added a comment - 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}
          Hide
          Matti Niemelä added a comment -

          Please review attached test case.

          Show
          Matti Niemelä added a comment - Please review attached test case.
          Hide
          Benjamin Eberlei added a comment - - edited

          Duplicate of DDC-839, and fixed.

          Show
          Benjamin Eberlei added a comment - - edited Duplicate of DDC-839 , and fixed.

            People

            • Assignee:
              Benjamin Eberlei
              Reporter:
              Matti Niemelä
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: