Doctrine 2 - ORM
  1. Doctrine 2 - ORM
  2. DDC-2701

Collections in originalEntityData gets over written

    Details

    • Type: Bug Bug
    • Status: Awaiting Feedback
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: ORM
    • Security Level: All
    • Labels:
      None

      Description

      I was trying to use the UnitOfWork::getOriginalEntityData method and i noticed that if there is a collection in that object, it changes in the uow original datas when i change it on my original object

      In my case the collection is ManyToMany unidirectional

      I think this happens because both the uow and the object work with the same reference to the collection
      maybe adding a clone of the collection instead of the real one, i don't know if it is possible

      Thank you for taking a look and for Doctrine!

      Tom

        Activity

        Thomas Klein created issue -
        Thomas Klein made changes -
        Field Original Value New Value
        Summary Collections in originalEntityData gets over writter Collections in originalEntityData gets over written
        Hide
        Marco Pivetta added a comment -

        This issue needs a test case in order to be reproduced.

        Show
        Marco Pivetta added a comment - This issue needs a test case in order to be reproduced.
        Marco Pivetta made changes -
        Status Open [ 1 ] Awaiting Feedback [ 10000 ]
        Hide
        Thomas Klein added a comment -

        Here is how i get to this bug:

        2 entities with a many to many unidirectional relation:

        class Conversation
        {
            // ...........
        
            /**
             * @ORM\ManyToMany(targetEntity="User")
             * @ORM\JoinTable(name="Conversations_To_Users",
             *      joinColumns={@ORM\JoinColumn(name="conversation_id", referencedColumnName="id")},
             *      inverseJoinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")}
             * )
             *
             * @var ArrayCollection
             */
            private $users;
        
            public function getUsers()
            {
                return $this->users();
            }
        
            // .....
        }
        
        class User
        {
            // .....
        }
        

        Load the Conversation entity joined with the associated users: you get a Conversation entity with the users property filled with a PersistentCollection initialized. You can find this object in the originalEntityData property of the unit of work.

        Then do:

        $users = $conversation->getUsers();
        $users->remove(0);
        

        In the conversation object, the first user of the collection has been removed. In the unit of work, find the corresponding object in the originalEntityData array and you'll see that the first user has been removed from the collection too. It is then not possible to use the originalEntityData on joined collection to check for changes

        Show
        Thomas Klein added a comment - Here is how i get to this bug: 2 entities with a many to many unidirectional relation: class Conversation { // ........... /** * @ORM\ManyToMany(targetEntity= "User" ) * @ORM\JoinTable(name= "Conversations_To_Users" , * joinColumns={@ORM\JoinColumn(name= "conversation_id" , referencedColumnName= "id" )}, * inverseJoinColumns={@ORM\JoinColumn(name= "user_id" , referencedColumnName= "id" )} * ) * * @ var ArrayCollection */ private $users; public function getUsers() { return $ this ->users(); } // ..... } class User { // ..... } Load the Conversation entity joined with the associated users: you get a Conversation entity with the users property filled with a PersistentCollection initialized. You can find this object in the originalEntityData property of the unit of work. Then do: $users = $conversation->getUsers(); $users->remove(0); In the conversation object, the first user of the collection has been removed. In the unit of work, find the corresponding object in the originalEntityData array and you'll see that the first user has been removed from the collection too. It is then not possible to use the originalEntityData on joined collection to check for changes
        Hide
        Marco Pivetta added a comment - - edited

        Thomas Klein still not sure what the problem is:

        $user         = new User();
        $conversation = new Conversation();
        
        $conversation->addUser($user);
        
        $em->persist($user);
        $em->persist($conversation);
        $em->flush();
        $em->clear();
        
        $conversation1   = $em->find('Conversation', $conversation->id);
        $usersCollection = $conversation->getUsers();
        
        $usersCollection->remove(0);
        
        var_dump($em->getUnitOfWork()->getOriginalEntityData($conversation1));
        
        // what is failing after this point?
        

        This is also how a test case should look like when thrown at the ORM functional test suite: https://github.com/doctrine/doctrine2/tree/v2.4.4/tests/Doctrine/Tests/ORM/Functional/Ticket

        Show
        Marco Pivetta added a comment - - edited Thomas Klein still not sure what the problem is: $user = new User(); $conversation = new Conversation(); $conversation->addUser($user); $em->persist($user); $em->persist($conversation); $em->flush(); $em->clear(); $conversation1 = $em->find('Conversation', $conversation->id); $usersCollection = $conversation->getUsers(); $usersCollection->remove(0); var_dump($em->getUnitOfWork()->getOriginalEntityData($conversation1)); // what is failing after this point? This is also how a test case should look like when thrown at the ORM functional test suite: https://github.com/doctrine/doctrine2/tree/v2.4.4/tests/Doctrine/Tests/ORM/Functional/Ticket
        Hide
        Thomas Klein added a comment -

        Sorry i'll try to write a real test case asap ...

        What is failing after this point ?

        $origConversationData = $em->getUnitOfWork()->getOriginalEntityData($conversation1);
        var_dump(count($origConversationData['users'])); // should be equal to 1, i get 0 ...
        

        The only diff i can see with your example is that i'm not using find but a query with the relation fetched-joined in it so the collection is initialized

        Show
        Thomas Klein added a comment - Sorry i'll try to write a real test case asap ... What is failing after this point ? $origConversationData = $em->getUnitOfWork()->getOriginalEntityData($conversation1); var_dump(count($origConversationData['users'])); // should be equal to 1, i get 0 ... The only diff i can see with your example is that i'm not using find but a query with the relation fetched-joined in it so the collection is initialized
        Hide
        Marco Pivetta added a comment -

        Collections are not tracked within entity data, but separately in the UoW

        Show
        Marco Pivetta added a comment - Collections are not tracked within entity data, but separately in the UoW
        Hide
        Thomas Klein added a comment -

        Ok so i guess we can close this ticket if this is an expected behavior ...

        In your last comment, did you mean:
        Collections are not tracked within entity data, but each element of the collection is tracked separately in the UoW ?

        Anyway thanks for your help

        Show
        Thomas Klein added a comment - Ok so i guess we can close this ticket if this is an expected behavior ... In your last comment, did you mean: Collections are not tracked within entity data, but each element of the collection is tracked separately in the UoW ? Anyway thanks for your help

        This list may be incomplete, as errors occurred whilst retrieving source from linked applications:

        • Request to http://www.doctrine-project.org/fisheye/ failed: Error in remote call to 'FishEye 0 (http://www.doctrine-project.org/fisheye/)' (http://www.doctrine-project.org/fisheye) [AbstractRestCommand{path='/rest-service-fe/search-v1/crossRepositoryQuery', params={query=DDC-2701, expand=changesets[0:20].revisions[0:29],reviews}, methodType=GET}] : Received status code 503 (Service Temporarily Unavailable)

          People

          • Assignee:
            Benjamin Eberlei
            Reporter:
            Thomas Klein
          • Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated: