Lazy loading OneToMany modifies the owning side #7263

Open
opened 2026-01-22 15:48:31 +01:00 by admin · 0 comments
Owner

Originally created by @FoxCie on GitHub (Nov 29, 2023).

Bug Report

Q A
BC Break no
Version 2.17.x (and also 3.0.x according to code)

Summary

When fetching a bidirectional OneToMany Collection with lazy fetch mode, all the elements are modified (that is, the property corresponding to the owning side of the element is set to the owner of the Collection).

Current behavior

Owning sides are modified during hydration (lazy-loading).

How to reproduce

Declare a bidirectional OneToMany, modify the owning side, then remove the element (for example) from the older association. The call to removeElement will hydrate the Collection, which will set the value of the owning side to its original value (the modification is lost).

This bug has been reported several times, at least in #6589, #7932 and probably in #6449 .

Expected behavior

Lazy loading should not modify anything apart from the Collection itself.

Discussion

As stated in some of the previous reports, the bug comes from https://github.com/doctrine/orm/blob/2.17.x/lib/Doctrine/ORM/PersistentCollection.php#L169 .

In the hydrateAdd of PersistentCollection, the owning side is willingly set to the owner :

$this->typeClass->reflFields[$this->backRefFieldName]->setValue(
                $element,
                $this->owner
            );

The comment above states that : If _backRefFieldName is set and its a one-to-many association, we need to set the back reference., but I do not understand why this should happen. Why would hydrating a Collection have anything to do with the owning side? Wouldn't the entity be already populated anyway, either because it was fetched before the call that caused hydration, or because the hydration process already loaded it (since it is being appended to the Collection, it must have been loaded in some way)?

In any way this totally breaks the transparent behaviour of lazy-loading, since (as demonstrated by the tests in the other reports linked above) changing the fetch mode to eager for instance solves this issue.

So my question is : is it safe to just remove these lines and not modify the owning side, or are there some side effects that I am not aware of?

From my point of view, if these lines are added to populate a lazy-loaded ManyToOne (the owning side), why not let the lazy-loading process of the owning side happen when require? And if it is not lazy-loaded, then why bother at all, since it must have been loaded previously?

Originally created by @FoxCie on GitHub (Nov 29, 2023). ### Bug Report | Q | A |------------ | ------ | BC Break | no | Version | 2.17.x (and also 3.0.x according to code) #### Summary When fetching a bidirectional OneToMany Collection with lazy fetch mode, all the elements are modified (that is, the property corresponding to the owning side of the element is set to the owner of the Collection). #### Current behavior Owning sides are modified during hydration (lazy-loading). #### How to reproduce Declare a bidirectional OneToMany, modify the owning side, then remove the element (for example) from the older association. The call to `removeElement` will hydrate the Collection, which will set the value of the owning side to its original value (the modification is lost). This bug has been reported several times, at least in #6589, #7932 and probably in #6449 . #### Expected behavior Lazy loading should not modify anything apart from the Collection itself. #### Discussion As stated in some of the previous reports, the bug comes from https://github.com/doctrine/orm/blob/2.17.x/lib/Doctrine/ORM/PersistentCollection.php#L169 . In the `hydrateAdd` of `PersistentCollection`, the owning side is willingly set to the owner : ```php $this->typeClass->reflFields[$this->backRefFieldName]->setValue( $element, $this->owner ); ``` The comment above states that : `If _backRefFieldName is set and its a one-to-many association, we need to set the back reference.`, but I do not understand why this should happen. Why would hydrating a Collection have anything to do with the owning side? Wouldn't the entity be already populated anyway, either because it was fetched before the call that caused hydration, or because the hydration process already loaded it (since it is being appended to the Collection, it must have been loaded in some way)? In any way this totally breaks the transparent behaviour of lazy-loading, since (as demonstrated by the tests in the other reports linked above) changing the fetch mode to eager for instance solves this issue. So my question is : is it safe to just remove these lines and not modify the owning side, or are there some side effects that I am not aware of? From my point of view, if these lines are added to populate a lazy-loaded ManyToOne (the owning side), why not let the lazy-loading process of the owning side happen when require? And if it is not lazy-loaded, then why bother at all, since it must have been loaded previously?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#7263