Refetching entity with eager relation overwrites nulled relation #6500

Open
opened 2026-01-22 15:34:08 +01:00 by admin · 2 comments
Owner

Originally created by @SiebelsTim on GitHub (Jul 8, 2020).

Bug Report

Q A
BC Break no?
Version 2.7.3

Summary

When setting a ManyToOne-Relation to null, and refetching the same entity, the original value of that relation is restored.
Refetching can happen, e.g. using findOneBy or even when lazily loading a collection where that entity is contained.
This problem only occurs when the fetch mode is eager.

Current behavior

Assume two entites: Bug and User. Every Bug has one optional Reporter which is a User.
Bug::reporter has fetch="EAGER".

Now when I want to clear the reporter field of a Bug using $bug->setReporter(null) and fetching that entity again before flushing, the bug's original reporter is restored in that bug. An eventual flush will not set the bug's reporter to null as doctrine sees an unchanged entity.

How to reproduce

I prepared a reproduction case based on the doctrine tutorial here: https://github.com/SiebelsTim/doctrine-bug-repro

It is the "current behaviour"-section put into code. The README contains step-by-step instructions on how to reproduce this. The latest commit shows the changes I made for this.

<?php
// test.php
require_once "bootstrap.php";

$bugRepo = $entityManager->getRepository(Bug::class);

// Fetch bug with existing reporter
$bug = $bugRepo->findOneBy(['description' => 'Something does not work!']);

echo "This is the name of the reporter currently assigned: ", PHP_EOL;
var_dump($bug->getReporter()->getName());

echo "\nWe've set the reporter to null.", PHP_EOL;
$bug->setReporter(null);

echo "Reporter is really null: ", PHP_EOL;
var_dump($bug->getReporter());

echo "\nWe refetch the same entity with the following description", PHP_EOL;
var_dump($bugRepo->findOneBy(['description' => 'Something does not work!'])->getDescription());

// We could replace the above line with something like $users->getBugs()->toArray() and the same problem occurs

echo "\nNow reporter should still be null: ", PHP_EOL;
if ($bug->getReporter() === null) {
    echo "it is null"; // We get here when fetch=LAZY
} else {
    echo "oh no, it is non-null again."; // We get here when fetch=EAGER
}

This only happens when fetch=EAGER and when setting the reporter to null. Setting a different non-null value will not be overwritten.

As a result, I think seemingly valid code is prone to this problem:

$bug->setReporter(null);
$bug->setNumber($users->getBugs()->count()); // lazy loading could reset the reporter

Expected behavior

Fetch mode and lazy-loading does not reset changes made to entities.

Originally created by @SiebelsTim on GitHub (Jul 8, 2020). ### Bug Report | Q | A |------------ | ------ | BC Break | no? | Version | 2.7.3 #### Summary When setting a ManyToOne-Relation to null, and refetching the same entity, the original value of that relation is restored. Refetching can happen, e.g. using findOneBy or even when lazily loading a collection where that entity is contained. This problem only occurs when the fetch mode is eager. #### Current behavior Assume two entites: Bug and User. Every Bug has one optional Reporter which is a User. `Bug::reporter` has `fetch="EAGER"`. Now when I want to clear the `reporter` field of a Bug using `$bug->setReporter(null)` and fetching that entity again before flushing, the bug's original reporter is restored in that bug. An eventual flush will not set the bug's reporter to null as doctrine sees an unchanged entity. #### How to reproduce I prepared a reproduction case based on the [doctrine tutorial](https://www.doctrine-project.org/projects/doctrine-orm/en/current/tutorials/getting-started.html) here: https://github.com/SiebelsTim/doctrine-bug-repro It is the "current behaviour"-section put into code. The README contains step-by-step instructions on how to reproduce this. The latest commit shows the changes I made for this. ```php <?php // test.php require_once "bootstrap.php"; $bugRepo = $entityManager->getRepository(Bug::class); // Fetch bug with existing reporter $bug = $bugRepo->findOneBy(['description' => 'Something does not work!']); echo "This is the name of the reporter currently assigned: ", PHP_EOL; var_dump($bug->getReporter()->getName()); echo "\nWe've set the reporter to null.", PHP_EOL; $bug->setReporter(null); echo "Reporter is really null: ", PHP_EOL; var_dump($bug->getReporter()); echo "\nWe refetch the same entity with the following description", PHP_EOL; var_dump($bugRepo->findOneBy(['description' => 'Something does not work!'])->getDescription()); // We could replace the above line with something like $users->getBugs()->toArray() and the same problem occurs echo "\nNow reporter should still be null: ", PHP_EOL; if ($bug->getReporter() === null) { echo "it is null"; // We get here when fetch=LAZY } else { echo "oh no, it is non-null again."; // We get here when fetch=EAGER } ``` This only happens when `fetch=EAGER` and when setting the reporter to `null`. Setting a different non-null value will not be overwritten. As a result, I think seemingly valid code is prone to this problem: ``` $bug->setReporter(null); $bug->setNumber($users->getBugs()->count()); // lazy loading could reset the reporter ``` #### Expected behavior Fetch mode and lazy-loading does not reset changes made to entities.
Author
Owner

@flaushi commented on GitHub (Sep 17, 2020):

I rather think this is no bug but a feature. If you want to set reporter to null, do it and flush it. After flushing, a refetch should give correct results.

@flaushi commented on GitHub (Sep 17, 2020): I rather think this is no bug but a feature. If you want to set reporter to null, do it and flush it. After flushing, a refetch should give correct results.
Author
Owner

@SiebelsTim commented on GitHub (Sep 23, 2020):

But why would the fetch mode in the entity change the behaviour?

@SiebelsTim commented on GitHub (Sep 23, 2020): But why would the fetch mode in the entity change the behaviour?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6500