Deprecate EntityManager::flush with $entity argument #6624

Closed
opened 2026-01-22 15:35:53 +01:00 by admin · 29 comments
Owner

Originally created by @beberlei on GitHub (Feb 6, 2021).

The method EntityManager::flush has an optional argument $entity (either accepting a single entity or array of entities) that when passed are the only set of entities considered in compute changeset operation. The idea originally was to allow more performant flush operations when a lot of entities are in the identity map.

However due to restrictions in how the UnitOfWork operates, it had to also "flush" other entities sometimes that were not passed to flush, making the operation inconsistent.

As such we decided to deprecate this approach to flushing and only allow a full flush for now.

Alternative: If you want to keep the set of entities small that flush operates on, you could decide to use the explicit change tracking policy for an entity instead of the implicit one. https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/reference/change-tracking-policies.html

Originally created by @beberlei on GitHub (Feb 6, 2021). The method `EntityManager::flush` has an optional argument $entity (either accepting a single entity or array of entities) that when passed are the only set of entities considered in compute changeset operation. The idea originally was to allow more performant flush operations when a lot of entities are in the identity map. However due to restrictions in how the UnitOfWork operates, it had to also "flush" other entities sometimes that were not passed to `flush`, making the operation inconsistent. As such we decided to deprecate this approach to flushing and only allow a full flush for now. **Alternative**: If you want to keep the set of entities small that flush operates on, you could decide to use the explicit change tracking policy for an entity instead of the implicit one. https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/reference/change-tracking-policies.html
admin added the Deprecation label 2026-01-22 15:35:53 +01:00
admin closed this issue 2026-01-22 15:35:53 +01:00
Author
Owner

@miedzikd commented on GitHub (Sep 6, 2021):

Hello @beberlei :)

Can you give me another tip, how can I solve my issue when flushing objects as parameters, will it be deprecated?

I have one huge table with Orders. It's used in a lot of places in the system.

Normally we use ->flush() without passing Order object as a parameter, but I have one command job that regenerates all Orders. Im doing that in batch for 1000 orders per execution.

If I pass Order object to flush - whole execution take more or less 60s. Without that 1200s, because size of unitOfWork is too huge.

Explicit change tracking policy can be apply on whole entity level only? I can't change this because it is used in too many places. Can I apply the policy only in one action? or one command?

Greetings from Poland :)

@miedzikd commented on GitHub (Sep 6, 2021): Hello @beberlei :) Can you give me another tip, how can I solve my issue when flushing objects as parameters, will it be deprecated? I have one huge table with Orders. It's used in a lot of places in the system. Normally we use `->flush()` without passing Order object as a parameter, but I have one command job that regenerates all Orders. Im doing that in batch for 1000 orders per execution. If I pass Order object to flush - whole execution take more or less 60s. Without that 1200s, because size of unitOfWork is too huge. Explicit change tracking policy can be apply on whole entity level only? I can't change this because it is used in too many places. Can I apply the policy only in one action? or one command? Greetings from Poland :)
Author
Owner

@wadjeroudi commented on GitHub (Nov 21, 2021):

@beberlei @Ocramius any news on this deprecation ? We should be able to do a specific data change at least in a new transaction without flushing the whole changes.
Maybe some sort of temporary clear() on entityManager and restore the unit of work after :

$em->saveStateAndClear() // temporary reset
$em->persist($entity)
$em->flush() // do not auto restore state in case multiple flushes are needed
$em->restorePreviousState()

I'm probably wrong but adding a property on EntityManager $savedUnitOfWork to store the current uof, instantiate a new empty one seems pretty simple imho :

$this->unitOfWork        = new UnitOfWork($this);
@wadjeroudi commented on GitHub (Nov 21, 2021): @beberlei @Ocramius any news on this deprecation ? We should be able to do a specific data change at least in a new transaction without flushing the whole changes. Maybe some sort of temporary clear() on entityManager and restore the unit of work after : ``` $em->saveStateAndClear() // temporary reset $em->persist($entity) $em->flush() // do not auto restore state in case multiple flushes are needed $em->restorePreviousState() ``` I'm probably wrong but adding a property on EntityManager $savedUnitOfWork to store the current uof, instantiate a new empty one seems pretty simple imho : ``` $this->unitOfWork = new UnitOfWork($this); ```
Author
Owner

@poolerMF commented on GitHub (Dec 22, 2021):

@beberlei @Ocramius any news on this deprecation ? We should be able to do a specific data change at least in a new transaction without flushing the whole changes. Maybe some sort of temporary clear() on entityManager and restore the unit of work after :

$em->saveStateAndClear() // temporary reset
$em->persist($entity)
$em->flush() // do not auto restore state in case multiple flushes are needed
$em->restorePreviousState()

I'm probably wrong but adding a property on EntityManager $savedUnitOfWork to store the current uof, instantiate a new empty one seems pretty simple imho :

$this->unitOfWork        = new UnitOfWork($this);

+1

@poolerMF commented on GitHub (Dec 22, 2021): > @beberlei @Ocramius any news on this deprecation ? We should be able to do a specific data change at least in a new transaction without flushing the whole changes. Maybe some sort of temporary clear() on entityManager and restore the unit of work after : > > ``` > $em->saveStateAndClear() // temporary reset > $em->persist($entity) > $em->flush() // do not auto restore state in case multiple flushes are needed > $em->restorePreviousState() > ``` > > I'm probably wrong but adding a property on EntityManager $savedUnitOfWork to store the current uof, instantiate a new empty one seems pretty simple imho : > > ``` > $this->unitOfWork = new UnitOfWork($this); > ``` +1
Author
Owner

@Fedik commented on GitHub (Dec 23, 2021):

We should be able to do a specific data change at least in a new transaction without flushing the whole changes.

It already possible, if I right understood the linked doc.
We have to define DEFERRED_EXPLICIT for ChangeTrackingPolicy.
Then flush() operation will take in count only an entities that was explicitly marked to "persist" with $em->persist($entity).

Old:

$em->persist($entity);
$em->flush($entity);

New:
Set mapping to DEFERRED_EXPLICIT, and:

$em->persist($entity);
$em->flush();
@Fedik commented on GitHub (Dec 23, 2021): > We should be able to do a specific data change at least in a new transaction without flushing the whole changes. It already possible, if I right understood the linked doc. We have to define `DEFERRED_EXPLICIT` for ChangeTrackingPolicy. Then `flush()` operation will take in count only an entities that was explicitly marked to "persist" with `$em->persist($entity)`. Old: ```php $em->persist($entity); $em->flush($entity); ``` New: Set mapping to `DEFERRED_EXPLICIT`, and: ```php $em->persist($entity); $em->flush(); ```
Author
Owner

@GDXbsv commented on GitHub (Dec 23, 2021):

@Fedik I see a problem with it:
If you not set DEFERRED_EXPLICIT as default and only one possible value for all entities, then if some other entity was changed in a different place without DEFERRED_EXPLICIT then this entity will be saved as well. So we still can not prevent flushing unexpected entities.

@GDXbsv commented on GitHub (Dec 23, 2021): @Fedik I see a problem with it: If you not set DEFERRED_EXPLICIT as default and only one possible value for all entities, then if some other entity was changed in a different place without DEFERRED_EXPLICIT then this entity will be saved as well. So we still can not prevent flushing unexpected entities.
Author
Owner

@Fedik commented on GitHub (Dec 23, 2021):

That correct, and that why you have to be sure all your Entities is set to DEFERRED_EXPLICIT(or NOTIFY) and that you call $em->persist($entity) when changes need to be written.
Unless your App requires different. I think.

@Fedik commented on GitHub (Dec 23, 2021): That correct, and that why you have to be sure all your Entities is set to DEFERRED_EXPLICIT(or NOTIFY) and that you call `$em->persist($entity)` when changes need to be written. Unless your App requires different. I think.
Author
Owner

@aniolekx commented on GitHub (Feb 21, 2022):

Is it possible to set ChangeTrackingPolicy per script execution? I am happy enough that whole app is set to Deferred Implicit, but for data upload from excel files I need Deferred Explicit, removing of that option will add me a lot of work (I will have to check whole app where we have missing persist)

@aniolekx commented on GitHub (Feb 21, 2022): Is it possible to set ChangeTrackingPolicy per script execution? I am happy enough that whole app is set to Deferred Implicit, but for data upload from excel files I need Deferred Explicit, removing of that option will add me a lot of work (I will have to check whole app where we have missing persist)
Author
Owner

@derrabus commented on GitHub (May 11, 2022):

Fixed via #9485.

@derrabus commented on GitHub (May 11, 2022): Fixed via #9485.
Author
Owner

@kgasienica commented on GitHub (Aug 9, 2022):

There were any discussion about that? This change makes many scripts in batches last way much longer. And what is even worse, since this change you can no longer be sure which things are flushed.

If I have to be honest, I don't really like this change.

@kgasienica commented on GitHub (Aug 9, 2022): There were any discussion about that? This change makes many scripts in batches last way much longer. And what is even worse, since this change you can no longer be sure which things are flushed. If I have to be honest, I don't really like this change.
Author
Owner

@beberlei commented on GitHub (Aug 9, 2022):

@kgasienica its deprecated yes, but we also plan to introduce an alternative that is better.

Just keep using the deprecated way until then

@beberlei commented on GitHub (Aug 9, 2022): @kgasienica its deprecated yes, but we also plan to introduce an alternative that is better. Just keep using the deprecated way until then
Author
Owner

@kgasienica commented on GitHub (Aug 9, 2022):

Okay, thank you. It is really breaking-change thing that is changing the way entities have to be managed in our applications.

@kgasienica commented on GitHub (Aug 9, 2022): Okay, thank you. It is really breaking-change thing that is changing the way entities have to be managed in our applications.
Author
Owner

@wadjeroudi commented on GitHub (Oct 7, 2022):

@beberlei any information on the new alternative please ?

@wadjeroudi commented on GitHub (Oct 7, 2022): @beberlei any information on the new alternative please ?
Author
Owner

@wadjeroudi commented on GitHub (Jan 19, 2023):

@beberlei @greg0ire it seems that the alternative was this one but @Ocramius won't work on it anymore by lack of time I guess.
https://github.com/doctrine/orm/issues/5550#issuecomment-1301849227

Still same approach ? or an other is considered ?

@wadjeroudi commented on GitHub (Jan 19, 2023): @beberlei @greg0ire it seems that the alternative was this one but @Ocramius won't work on it anymore by lack of time I guess. https://github.com/doctrine/orm/issues/5550#issuecomment-1301849227 Still same approach ? or an other is considered ?
Author
Owner

@phpws commented on GitHub (Sep 26, 2023):

there are probably cases that I can't think of where this wouldn't work, but thought to share it incase it could be useful.

If the existing flush method was renamed to something like flushAll and made private and passing in an entity was removed from the method, then the flush method was replaced with this, then it seems like this could work, at least for my case where I am using it does, but maybe you will know reasons as to why it can't be done like this, maybe it would still be inconsistant in some cases.

    public function flush(?object $object = null): void
    {
        if ($object === null) {
            $this->flushAll();
            return;
        }

        $class = \get_class($object);

        $unitOfWork = $this->getUnitOfWork();
        $scheduledEntities = $unitOfWork->getScheduledEntityInsertions();

        foreach ($scheduledEntities as $scheduledEntity) {
            if (\get_class($scheduledEntity) != $class) {
                $unitOfWork->remove($scheduledEntity);
            }
        }

        $this->flushAll();

        foreach ($scheduledEntities as $scheduledEntity) {
            if (\get_class($scheduledEntity) != $class) {
                $unitOfWork->persist($scheduledEntity);
            }
        }
    }
@phpws commented on GitHub (Sep 26, 2023): there are probably cases that I can't think of where this wouldn't work, but thought to share it incase it could be useful. If the existing flush method was renamed to something like `flushAll` and made private and passing in an entity was removed from the method, then the flush method was replaced with this, then it seems like this could work, at least for my case where I am using it does, but maybe you will know reasons as to why it can't be done like this, maybe it would still be inconsistant in some cases. ``` public function flush(?object $object = null): void { if ($object === null) { $this->flushAll(); return; } $class = \get_class($object); $unitOfWork = $this->getUnitOfWork(); $scheduledEntities = $unitOfWork->getScheduledEntityInsertions(); foreach ($scheduledEntities as $scheduledEntity) { if (\get_class($scheduledEntity) != $class) { $unitOfWork->remove($scheduledEntity); } } $this->flushAll(); foreach ($scheduledEntities as $scheduledEntity) { if (\get_class($scheduledEntity) != $class) { $unitOfWork->persist($scheduledEntity); } } } ```
Author
Owner

@beberlei commented on GitHub (Sep 27, 2023):

@phpws Please don't do that :-)

@beberlei commented on GitHub (Sep 27, 2023): @phpws Please don't do that :-)
Author
Owner

@phpws commented on GitHub (Sep 29, 2023):

@beberlei ok, I didn't in the end, don't worry :-D, I take it that would have adverse affects on other things, performance for one would probably be a problem on large projects with huge sets of scheduledEntites collections, also it doesn't account for updates.

@phpws commented on GitHub (Sep 29, 2023): @beberlei ok, I didn't in the end, don't worry :-D, I take it that would have adverse affects on other things, performance for one would probably be a problem on large projects with huge sets of scheduledEntites collections, also it doesn't account for updates.
Author
Owner

@wadjeroudi commented on GitHub (Jan 29, 2024):

@beberlei @greg0ire you released the doctrine 3.0 RC1, congrats, what about this BC BREAK
https://github.com/doctrine/orm/blob/3.0.x/UPGRADE.md#bc-break-removed-ability-to-partially-flushcommit-entity-manager-and-unit-of-work

there's still no alternative ?

@wadjeroudi commented on GitHub (Jan 29, 2024): @beberlei @greg0ire you released the doctrine 3.0 RC1, congrats, what about this BC BREAK https://github.com/doctrine/orm/blob/3.0.x/UPGRADE.md#bc-break-removed-ability-to-partially-flushcommit-entity-manager-and-unit-of-work there's still no alternative ?
Author
Owner

@greg0ire commented on GitHub (Jan 29, 2024):

@wadjeroudi I'm told that using detach() (it's been undeprecated) would be an alternative for you, as partial flushing should be equivalent to detaching things you don't want to flush and then calling flush()

@greg0ire commented on GitHub (Jan 29, 2024): @wadjeroudi I'm told that using `detach()` (it's been [undeprecated](https://github.com/doctrine/orm/pull/8978)) would be an alternative for you, as partial flushing should be equivalent to detaching things you don't want to `flush` and then calling `flush()`
Author
Owner

@wokenlex commented on GitHub (Apr 5, 2024):

So, the real way to work with it normally, is to make handwritten queries, like in old times?

$stmt = $this->em->getConnection()->prepare('');
$stmt->executeStatement();

I mean, sometimes you need to do something under the hood - write do database log, cache some items, but you don't wan't to ruin the main business logic.

@wokenlex commented on GitHub (Apr 5, 2024): So, the real way to work with it normally, is to make handwritten queries, like in old times? ``` $stmt = $this->em->getConnection()->prepare(''); $stmt->executeStatement(); ``` I mean, sometimes you need to do something under the hood - write do database log, cache some items, but you don't wan't to ruin the main business logic.
Author
Owner

@CsabaNa commented on GitHub (Jun 12, 2024):

So how can we insert/flush only one Entity e.g. I need a record ID in multiple places but based on this record some other entities could change so I don't want to inc the server load to insert one and update the others Then again update several records so updating at least twice i would insert one do the stuff and update once....

@CsabaNa commented on GitHub (Jun 12, 2024): So how can we insert/flush only one Entity e.g. I need a record ID in multiple places but based on this record some other entities could change so I don't want to inc the server load to insert one and update the others Then again update several records so updating at least twice i would insert one do the stuff and update once....
Author
Owner

@thereisnobugs commented on GitHub (Oct 29, 2024):

@kgasienica its deprecated yes, but we also plan to introduce an alternative that is better.

And... where is an alternative?

@thereisnobugs commented on GitHub (Oct 29, 2024): > @kgasienica its deprecated yes, but we also plan to introduce an alternative that is better. And... where is an alternative?
Author
Owner

@yakobe commented on GitHub (Mar 21, 2025):

@kgasienica its deprecated yes, but we also plan to introduce an alternative that is better.

Just keep using the deprecated way until then

@beberlei Please could you provide some information on the better alternative? Many thanks.

@yakobe commented on GitHub (Mar 21, 2025): > [@kgasienica](https://github.com/kgasienica?rgh-link-date=2022-08-09T07%3A57%3A31.000Z) its deprecated yes, but we also plan to introduce an alternative that is better. > > Just keep using the deprecated way until then @beberlei Please could you provide some information on the better alternative? Many thanks.
Author
Owner

@jurchiks commented on GitHub (May 27, 2025):

This change is now causing me a lot of problems.
I never used flush($entity), but now I have a need for it, because I have some code that would greatly benefit from it due to some code being called tens of thousands of times in one cron job. I know that calling flush() tens of thousands of times would absolutely murder the performance that's already not great, and I don't need to flush everything, I only need to flush the changes that I want. At the same time, I obviously do not want to rewrite my entire codebase to manually flush everything, because that would be a ridiculous amount of work, all because some people thought that they know better than everyone else using their library.

However due to restrictions in how the UnitOfWork operates, it had to also "flush" other entities sometimes that were not passed to flush, making the operation inconsistent.

I don't care about this "problem", it doesn't affect my use case. I want to flush a single entity that does not affect any other entity (it has only parent relations to entities that already exist in the database, and they're one-way relations). This change has made it impossible, and your suggested "fix" is to add DEFERRED_EXPLICIT to all entities and manually persist them everywhere.

This is a shit change that does more bad than good for the people who had any use of flush($entity) at all. All you needed to do is add a disclaimer in the method's PHPDocs about the potential issue.

And it has been over 3 years since the promised "better alternative" that is yet to be seen. You should have implemented the "better alternative" before you removed this! This is backwards as hell!

@jurchiks commented on GitHub (May 27, 2025): This change is now causing me a lot of problems. I never used `flush($entity)`, but now I have a need for it, because I have some code that would greatly benefit from it due to some code being called tens of thousands of times in one cron job. I know that calling `flush()` tens of thousands of times would absolutely murder the performance that's already not great, and I don't need to flush everything, I only need to flush the changes that I want. At the same time, I obviously do not want to rewrite my entire codebase to manually flush everything, because that would be a ridiculous amount of work, all because some people thought that they know better than everyone else using their library. > However due to restrictions in how the UnitOfWork operates, it had to also "flush" other entities sometimes that were not passed to flush, making the operation inconsistent. I don't care about this "problem", it doesn't affect my use case. I want to flush a single entity that does not affect any other entity (it has only parent relations to entities that already exist in the database, and they're one-way relations). This change has made it impossible, and your suggested "fix" is to add DEFERRED_EXPLICIT to _all_ entities and manually persist them _everywhere_. This is a shit change that does more bad than good for the people who had any use of `flush($entity)` at all. All you needed to do is add a disclaimer in the method's PHPDocs about the potential issue. And it has been over 3 years since the promised "better alternative" that is yet to be seen. You should have implemented the "better alternative" before you removed this! This is backwards as hell!
Author
Owner

@Ocramius commented on GitHub (May 27, 2025):

There's no alternative due to how the UnitOfWork is designed: you need to size your batch jobs accordingly.

The alternative is a different architectural pattern, not part of this ORM, and that is fine.

@Ocramius commented on GitHub (May 27, 2025): There's no alternative due to how the UnitOfWork is designed: you need to size your batch jobs accordingly. The alternative is a different architectural pattern, not part of this ORM, and that is **fine**.
Author
Owner

@jurchiks commented on GitHub (May 28, 2025):

So when beberlei wrote:

its deprecated yes, but we also plan to introduce an alternative that is better.
Just keep using the deprecated way until then

He lied? Did you ever actually plan on doing this? If yes, what changed? Why didn't you give an official commentary on the status of this thing here?
You can downvote me all you want - two can play this game -, but that doesn't save the situation at all.

@jurchiks commented on GitHub (May 28, 2025): So when beberlei wrote: > its deprecated yes, but we also _plan to introduce an alternative that is better_. Just keep using the deprecated way _until then_ He lied? Did you ever actually plan on doing this? If yes, what changed? Why didn't you give an official commentary on the status of this thing here? You can downvote me all you want - two can play this game -, but that doesn't save the situation at all.
Author
Owner

@beberlei commented on GitHub (May 29, 2025):

So when beberlei wrote:

its deprecated yes, but we also plan to introduce an alternative that is better.
Just keep using the deprecated way until then

He lied? Did you ever actually plan on doing this? If yes, what changed? Why didn't you give an official commentary on the status of this thing here?
You can downvote me all you want - two can play this game -, but that doesn't save the situation at all.

I still plan to do this, but all my time here is voluntary so 🤷 cant say when.

@beberlei commented on GitHub (May 29, 2025): > So when beberlei wrote: > > > its deprecated yes, but we also _plan to introduce an alternative that is better_. > Just keep using the deprecated way _until then_ > > He lied? Did you ever actually plan on doing this? If yes, what changed? Why didn't you give an official commentary on the status of this thing here? > You can downvote me all you want - two can play this game -, but that doesn't save the situation at all. I still plan to do this, but all my time here is voluntary so 🤷 cant say when.
Author
Owner

@jurchiks commented on GitHub (May 31, 2025):

Then you shouldn't have removed this feature and shouldn't have promised a replacement. 3+ years...

@jurchiks commented on GitHub (May 31, 2025): Then you shouldn't have removed this feature and shouldn't have promised a replacement. 3+ years...
Author
Owner

@Fipschen commented on GitHub (May 31, 2025):

@jurchiks I think it's time to cool down a bit. Don't forget, he's also just a human who guess respect.

@Fipschen commented on GitHub (May 31, 2025): @jurchiks I think it's time to cool down a bit. Don't forget, he's also just a human who guess respect.
Author
Owner

@Arkemlar commented on GitHub (Jul 28, 2025):

Actually, I agree with @jurchiks. Removing such kind of feature without replacement is painful and, honestly, not respectful to users - because it forces them to search for hacks. Multiply this problem by the number of affected users and... well.
It is fine to write notice right inside flush's phpdoc that doing flush($object) leads to inconsistency, etc., but let users decide. May be they know what they doing.

@Arkemlar commented on GitHub (Jul 28, 2025): Actually, I agree with @jurchiks. Removing such kind of feature without replacement is painful and, honestly, not respectful to users - because it forces them to search for _hacks_. Multiply this problem by the number of affected users and... well. It is fine to write notice right inside flush's phpdoc that doing `flush($object)` leads to inconsistency, etc., but let users decide. May be they know what they doing.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6624