Skip to content

Commit

Permalink
Merge commit 'a32578b7e' into 3.0.x
Browse files Browse the repository at this point in the history
  • Loading branch information
greg0ire committed Jan 17, 2024
2 parents 8aa6a5f + a32578b commit 8dec24d
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 24 deletions.
47 changes: 25 additions & 22 deletions docs/en/reference/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,19 @@ Events Overview
| :ref:`onClear<reference-events-on-clear>` | ``$em->clear()`` | No | `OnClearEventArgs`_ |
+-----------------------------------------------------------------+-----------------------+-----------+-------------------------------------+

.. warning::

Making changes to entities and calling ``EntityManager::flush()`` from within
event handlers dispatched by ``EntityManager::flush()`` itself is strongly
discouraged, and might be deprecated and eventually prevented in the future.

The reason is that it causes re-entrance into ``UnitOfWork::commit()`` while a commit
is currently being processed. The ``UnitOfWork`` was never designed to support this,
and its behavior in this situation is not covered by any tests.

This may lead to entity or collection updates being missed, applied only in parts and
changes being lost at the end of the commit phase.

Naming convention
~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -654,30 +667,33 @@ Restrictions for this event:
postUpdate, postRemove, postPersist
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

These three post* events are called inside ``EntityManager::flush()``.
These three ``post*`` events are called inside ``EntityManager::flush()``.
Changes in here are not relevant to the persistence in the
database, but you can use these events to alter non-persistable items,
like non-mapped fields, logging or even associated classes that are
not directly mapped by Doctrine.

- The ``postUpdate`` event occurs after the database
update operations to entity data. It is not called for a DQL
``UPDATE`` statement.
update operations to entity data, but before the database transaction
has been committed. It is not called for a DQL ``UPDATE`` statement.
- The ``postPersist`` event occurs for an entity after the entity has
been made persistent. It will be invoked after all database insert
operations for new entities have been performed. Generated primary
key values will be available for all entities at the time this
event is triggered.
operations for new entities have been performed, but before the database
transaction has been committed. Generated primary key values will be
available for all entities at the time this event is triggered.
- The ``postRemove`` event occurs for an entity after the
entity has been deleted. It will be invoked after all database
delete operations for entity rows have been executed. This event is
not called for a DQL ``DELETE`` statement.
delete operations for entity rows have been executed, but before the
database transaction has been committed. This event is not called for
a DQL ``DELETE`` statement.

.. note::

At the time ``postPersist`` is called, there may still be collection and/or
"extra" updates pending. The database may not yet be completely in
sync with the entity states in memory, not even for the new entities.
sync with the entity states in memory, not even for the new entities. Similarly,
also at the time ``postUpdate`` and ``postRemove`` are called, in-memory collections
may still be in a "dirty" state or still contain removed entities.

.. warning::

Expand All @@ -686,19 +702,6 @@ not directly mapped by Doctrine.
cascade remove relations. In this case, you should load yourself the proxy in
the associated ``pre*`` event.

.. warning::

Making changes to entities and calling ``EntityManager::flush()`` from within
``post*`` event handlers is strongly discouraged, and might be deprecated and
eventually prevented in the future.

The reason is that it causes re-entrance into ``UnitOfWork::commit()`` while a commit
is currently being processed. The ``UnitOfWork`` was never designed to support this,
and its behavior in this situation is not covered by any tests.

This may lead to entity or collection updates being missed, applied only in parts and
changes being lost at the end of the commit phase.

.. _reference-events-post-load:

postLoad
Expand Down
5 changes: 3 additions & 2 deletions src/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -2523,9 +2523,10 @@ public function createEntity(string $className, array $data, array &$hints = [])
$reflField->setValue($entity, $pColl);

if ($hints['fetchMode'][$class->name][$field] === ClassMetadata::FETCH_EAGER) {
if ($assoc->isOneToMany()) {
$isIteration = isset($hints[Query::HINT_INTERNAL_ITERATION]) && $hints[Query::HINT_INTERNAL_ITERATION];
if (! $isIteration && $assoc->isOneToMany()) {
$this->scheduleCollectionForBatchLoading($pColl, $class);
} elseif ($assoc->isManyToMany()) {
} elseif (($isIteration && $assoc->isOneToMany()) || $assoc->isManyToMany()) {
$this->loadCollection($pColl);
$pColl->takeSnapshot();
}
Expand Down
12 changes: 12 additions & 0 deletions tests/Tests/ORM/Functional/EagerFetchCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ public function testSubselectFetchJoinWithNotAllowed(): void
$query->getResult();
}

public function testEagerFetchWithIterable(): void
{
$this->createOwnerWithChildren(2);
$this->_em->flush();
$this->_em->clear();

$iterable = $this->_em->getRepository(EagerFetchOwner::class)->createQueryBuilder('o')->getQuery()->toIterable();
$owner = $iterable->current();

$this->assertCount(2, $owner->children);
}

protected function createOwnerWithChildren(int $children): EagerFetchOwner
{
$owner = new EagerFetchOwner();
Expand Down

0 comments on commit 8dec24d

Please sign in to comment.