Doctrine 2 - ORM
  1. Doctrine 2 - ORM
  2. DDC-2585

Persisting the owning side will not set the inverse side relationship

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Invalid
    • Affects Version/s: 2.3.4
    • Fix Version/s: None
    • Component/s: ORM
    • Security Level: All

      Description

      Adding items to the owning side of a bidirectional relationship should be enough to set the items' relationship to its owner. Instead, as of now we still have to manually set both sides, be it on the entity itself or on your controller/service code.

      Example:

      $owning->addObject($inverse);

      Doctrine should execute $inverse->setOwner($owning) when flushing $owning. Right now we have to run $owning->addObject($inverse) and $inverse->setOwner($owning) manually, which makes no sense, since Owner entity is configured to cascade persist operations.

        Activity

        Hide
        Marco Pivetta added a comment -

        Doctrine ORM is does just save and load VALID object graphs. It won't fix a broken object graph for you.

        Show
        Marco Pivetta added a comment - Doctrine ORM is does just save and load VALID object graphs. It won't fix a broken object graph for you.
        Hide
        Pedro Cordeiro added a comment - - edited

        I add multiple objects to the owning side's collection, persist it, and when I reload it, the collection is empty (as the objects were saved without referencing the owner). This is severely counter-intuitive. If I added items to an entity's collection, persisted the entity and the entity was configured to cascade persist operations for said collection, I would expect (and lots of other people too, as I found in my google searches) the items not only to be persisted, but to remain inside the collection (meaning they would have to reference its owner). Flushing the entity manager should NOT empty my entity collections. There is ABSOLUTELY no reason to have an ->addObject method in my entity, if I also have to call the ->setOwner method on said Object. Only calling the ->setOwner is enough to add it to the collection, but adding it to the collection is not enough to have its owner set and this is counter-intuitive.

        If I call $item->setContainer($container);, $item is automatically added to $container->items (after rehydrating $container); But calling $container->addItem($item); will not set $item->container to $container, which causes $container->items to be empty after a $entityManager->flush(); This is highly inconsistent, since $item->setContainer($container); adds the item to $container->items after I rehydrate $container. Doctrine already knows the items inside the collection are the inverse side and the entity that holds the collection is the owning side. There is no reason not to set the inverse-owning relationship (therefore destroying the owning-inverse relationship after a flush).

        Show
        Pedro Cordeiro added a comment - - edited I add multiple objects to the owning side's collection, persist it, and when I reload it, the collection is empty (as the objects were saved without referencing the owner). This is severely counter-intuitive. If I added items to an entity's collection, persisted the entity and the entity was configured to cascade persist operations for said collection, I would expect (and lots of other people too, as I found in my google searches) the items not only to be persisted, but to remain inside the collection (meaning they would have to reference its owner). Flushing the entity manager should NOT empty my entity collections. There is ABSOLUTELY no reason to have an ->addObject method in my entity, if I also have to call the ->setOwner method on said Object. Only calling the ->setOwner is enough to add it to the collection, but adding it to the collection is not enough to have its owner set and this is counter-intuitive. If I call $item->setContainer($container);, $item is automatically added to $container->items (after rehydrating $container); But calling $container->addItem($item); will not set $item->container to $container, which causes $container->items to be empty after a $entityManager->flush(); This is highly inconsistent, since $item->setContainer($container); adds the item to $container->items after I rehydrate $container. Doctrine already knows the items inside the collection are the inverse side and the entity that holds the collection is the owning side. There is no reason not to set the inverse-owning relationship (therefore destroying the owning-inverse relationship after a flush).
        Hide
        Marco Pivetta added a comment -

        Doctrine does not modify the state of your objects except for identifiers (during persistence) and hydration (when creating them).

        Regardless of Doctrine, you SHOULD keep your object graph consistent.

        Please take following example as a guideline: https://gist.github.com/3121916

        Additionally, if you clear your object manager and then reload your objects, you will notice they will actually be correctly loaded.

        Again, doctrine does NOT fix your object graph. It saves and loads valid state. It won't ever set the inverse side of any of your objects (ever) automagically.

        Show
        Marco Pivetta added a comment - Doctrine does not modify the state of your objects except for identifiers (during persistence) and hydration (when creating them). Regardless of Doctrine, you SHOULD keep your object graph consistent. Please take following example as a guideline: https://gist.github.com/3121916 Additionally, if you clear your object manager and then reload your objects, you will notice they will actually be correctly loaded. Again, doctrine does NOT fix your object graph. It saves and loads valid state. It won't ever set the inverse side of any of your objects (ever) automagically.
        Hide
        Pedro Cordeiro added a comment -

        Even though Doctrine won't fix the graph, it will load a fixed graph on the next hydration. If I call $item->setContainer($container), it will automagically add $item to $container->items, the next time I hydrate that entity. Only the opposite is not true: since adding $item to $container->items won't set $item->container, on the next reload my collection will be empty (even though I had added items before persisting/flushing).

        Doctrine will empty my collections on the next reload, if the collection's items' references to the owner are not set (obviously). You claim this is intended (for Doctrine probably shouldn't be changing my object states), but I think (and many people on stackoverflow and on the groups seem to agree) this causes a lot of referential integrity issues. I just don't see why I'd ever call $container->addItem, if it still requires me to call $item->setContainer($container) – and calling $item->setContainer($container) alone would have the same effect (even though it would keep my currently loaded graph out-of-sync with the database, the graph would be correctly reloaded on the next page hit, which seems to almost always be the case). I also don't see the issue of having Doctrine update the container property on each item, if they were added to $container->items.

        I already got that Doctrine won't do it. I'd love if you could ellaborate on why Doctrine shouldn't do it.

        Thank you.

        Show
        Pedro Cordeiro added a comment - Even though Doctrine won't fix the graph, it will load a fixed graph on the next hydration. If I call $item->setContainer($container), it will automagically add $item to $container->items, the next time I hydrate that entity. Only the opposite is not true: since adding $item to $container->items won't set $item->container, on the next reload my collection will be empty (even though I had added items before persisting/flushing). Doctrine will empty my collections on the next reload, if the collection's items' references to the owner are not set (obviously). You claim this is intended (for Doctrine probably shouldn't be changing my object states), but I think (and many people on stackoverflow and on the groups seem to agree) this causes a lot of referential integrity issues. I just don't see why I'd ever call $container->addItem, if it still requires me to call $item->setContainer($container) – and calling $item->setContainer($container) alone would have the same effect (even though it would keep my currently loaded graph out-of-sync with the database, the graph would be correctly reloaded on the next page hit, which seems to almost always be the case). I also don't see the issue of having Doctrine update the container property on each item, if they were added to $container->items. I already got that Doctrine won't do it. I'd love if you could ellaborate on why Doctrine shouldn't do it. Thank you.
        Hide
        Marco Pivetta added a comment -

        The point is that Doctrine ORM doesn't modify your objects. It's just a DB-based very complex serializer, if you want to see it that way.

        Show
        Marco Pivetta added a comment - The point is that Doctrine ORM doesn't modify your objects. It's just a DB-based very complex serializer, if you want to see it that way.
        Hide
        Christophe Coevoet added a comment -

        The doc explicitly says that Doctrine does not track changes on the inverse side. So if you want to save the relation, your object graph has to set the owning side. Tracking on both sides would be slower and would cause huge issues (what would happen if changes done on the owning side and the inverse side are incompatible ?)

        The best way to make the change transparent to other parts of your project is to handle it in the adder:

        class Container
        {
            public method addItem(Item $item)
            {
                $item->setContainer($this);
                $this->items[] = $item;
            }
        }
        
        Show
        Christophe Coevoet added a comment - The doc explicitly says that Doctrine does not track changes on the inverse side. So if you want to save the relation, your object graph has to set the owning side. Tracking on both sides would be slower and would cause huge issues (what would happen if changes done on the owning side and the inverse side are incompatible ?) The best way to make the change transparent to other parts of your project is to handle it in the adder: class Container { public method addItem(Item $item) { $item->setContainer($ this ); $ this ->items[] = $item; } }

          People

          • Assignee:
            Marco Pivetta
            Reporter:
            Pedro Cordeiro
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: