Within phase of completing hydratation is PostLoad callback launched on inappropriate entity #6003

Closed
opened 2026-01-22 15:24:34 +01:00 by admin · 3 comments
Owner

Originally created by @cejen on GitHub (Jun 28, 2018).

Originally assigned to: @Ocramius on GitHub.

Bug Report

Q A
BC Break yes
Version 2.5.14

Summary

When entity hydratation is completed on child entity within hydratation of parent entity, PostLoad callback (and probably all callbacks) of parent entity is launched. Parent entity is not completely hydrated at this moment.

Current behavior

Quoted in bug summary

How to reproduce

Model:

/**
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class EntityA {

    /**
     * @var EntityB
     * @ORM\ManyToOne(targetEntity="EntityB")
     * @ORM\JoinColumn(referencedColumnName="id", nullable=false)
     */
    protected $propB;

    /**
     * @var EntityC
     * @ORM\ManyToOne(targetEntity="EntityC")
     * @ORM\JoinColumn(referencedColumnName="id", nullable=false)
     */
    protected $propC;
 
    /**
     * @var EntityD
     * @ORM\ManyToOne(targetEntity="EntityD")
     * @ORM\JoinColumn(referencedColumnName="id", nullable=false)
     */
    protected $propD;

    /**
     * @var EntityD
     */
    protected $propDLoaded;

   ...
    /**
     * @ORM\PostLoad
     */
    public function postLoad()
    {
        $this->propDLoaded = $this->propD;
    }
}

/**
 * @ORM\Entity
 */
class EntityB {
    ....
}

/**
 * @ORM\Entity
 */
class EntityC {

    /**
     * @var EntityE
     * @ORM\ManyToOne(targetEntity="EntityE")
     * @ORM\JoinColumn(referencedColumnName="id", nullable=false)
     */
    protected $propE;

}

/**
 * @ORM\Entity
 */
class EntityD {
    ...
}

/**
 * @ORM\MappedSuperclass
 */
abstract class EntityE {
}

Issued DQL statement:

SELECT a, b, c, d
    FROM EntityA a
        JOIN a.propB b
        JOIN a.propC c
        JOIN a.propD d
    WHERE a.id = :id

Query result is hydrated from result set in this steps (in class \Doctrine\ORM\Internal\Hydration\ObjectHydrator):

  1. Parent class EnityA is hydrated
  2. Entity property propB is hydrated
  3. Entity property propC is hydrated. Because property propE of class EntityC is related to mapped superclass, extra SQL select is issued to load data of property propE (mapped superclass = cannot be proxied). When entity EntityE is completely hydrated then method ObjectHydrator::cleanup is launched. Within this method is launched $this->_uow->hydrationComplete(). It causes invoking method EntityA::postLoad, but entity is still incomplete at this moment (property propD wasn't already hydrated).

Expected behavior

Hydratation cleanup step 3 must be performed only for entity EntityE used to setup property propE. Hydratation cleanup step for entity EntityA must be performed after all properties are hydrated.

Originally created by @cejen on GitHub (Jun 28, 2018). Originally assigned to: @Ocramius on GitHub. ### Bug Report <!-- Fill in the relevant information below to help triage your issue. --> | Q | A |------------ | ------ | BC Break | yes | Version | 2.5.14 #### Summary When entity hydratation is completed on child entity within hydratation of parent entity, PostLoad callback (and probably all callbacks) of parent entity is launched. Parent entity is not completely hydrated at this moment. #### Current behavior Quoted in bug summary #### How to reproduce Model: ```php /** * @ORM\Entity * @ORM\HasLifecycleCallbacks */ class EntityA { /** * @var EntityB * @ORM\ManyToOne(targetEntity="EntityB") * @ORM\JoinColumn(referencedColumnName="id", nullable=false) */ protected $propB; /** * @var EntityC * @ORM\ManyToOne(targetEntity="EntityC") * @ORM\JoinColumn(referencedColumnName="id", nullable=false) */ protected $propC; /** * @var EntityD * @ORM\ManyToOne(targetEntity="EntityD") * @ORM\JoinColumn(referencedColumnName="id", nullable=false) */ protected $propD; /** * @var EntityD */ protected $propDLoaded; ... /** * @ORM\PostLoad */ public function postLoad() { $this->propDLoaded = $this->propD; } } /** * @ORM\Entity */ class EntityB { .... } /** * @ORM\Entity */ class EntityC { /** * @var EntityE * @ORM\ManyToOne(targetEntity="EntityE") * @ORM\JoinColumn(referencedColumnName="id", nullable=false) */ protected $propE; } /** * @ORM\Entity */ class EntityD { ... } /** * @ORM\MappedSuperclass */ abstract class EntityE { } ``` Issued DQL statement: ```php SELECT a, b, c, d FROM EntityA a JOIN a.propB b JOIN a.propC c JOIN a.propD d WHERE a.id = :id ``` Query result is hydrated from result set in this steps (in class ```\Doctrine\ORM\Internal\Hydration\ObjectHydrator```): 1. Parent class ```EnityA``` is hydrated 2. Entity property ```propB``` is hydrated 3. Entity property ```propC``` is hydrated. Because property ```propE``` of class ```EntityC``` is related to mapped superclass, extra SQL select is issued to load data of property ```propE``` (mapped superclass = cannot be proxied). When entity ```EntityE``` is completely hydrated then method ```ObjectHydrator::cleanup``` is launched. Within this method is launched ```$this->_uow->hydrationComplete()```. It causes invoking method ```EntityA::postLoad```, but entity is still incomplete at this moment (property ```propD``` wasn't already hydrated). #### Expected behavior Hydratation cleanup step 3 must be performed only for entity ```EntityE``` used to setup property ```propE```. Hydratation cleanup step for entity ```EntityA``` must be performed after all properties are hydrated.
admin added the BugWon't Fix labels 2026-01-22 15:24:34 +01:00
admin closed this issue 2026-01-22 15:24:35 +01:00
Author
Owner

@Ocramius commented on GitHub (Jun 28, 2018):

Please verify if this is still valid on 2.6.x: unless there is a security issue, ORM 2.5.x won't be touched anymore.

@Ocramius commented on GitHub (Jun 28, 2018): Please verify if this is still valid on `2.6.x`: unless there is a security issue, ORM `2.5.x` won't be touched anymore.
Author
Owner

@cejen commented on GitHub (Jun 28, 2018):

We cannot use version 2.6.x because our customer use PHP 5.6. If version 2.5.x won't be touched we try to find any workaround. Delete this issue please. Thanks for your response.

@cejen commented on GitHub (Jun 28, 2018): We cannot use version ```2.6.x``` because our customer use PHP 5.6. If version ```2.5.x``` won't be touched we try to find any workaround. Delete this issue please. Thanks for your response.
Author
Owner

@Ocramius commented on GitHub (Jun 28, 2018):

Please see https://secure.php.net/supported-versions.php - PHP 5.x is being retired this year, so please make pressure on your client to update to something current.

Closing here meanwhile.

@Ocramius commented on GitHub (Jun 28, 2018): Please see https://secure.php.net/supported-versions.php - PHP 5.x is being retired this year, so please make pressure on your client to update to something current. Closing here meanwhile.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6003