Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doctrine PostLoad Lifecycle Callback not executed in fetchJoin

I have a problem with a Lifecycle Callback of Doctrine ORM, that is not executed on fetch-joined entities, while it's regularly executed on entities that are lazy-loaded.

This is the code:

EntityA:

namespace AppBundle\Entity\EntityA;

use Doctrine\ORM\Mapping as ORM;

/**
 * EntityA
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks()
 */
class EntityA {
    /**
     * @var int
     * @ORM\Id
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $name;

    /**
     * @var \Doctrine\Common\Collections\ArrayCollection
     * @ORM\OneToMany(targetEntity="EntityB", mappedBy="EntityA", indexBy="name", cascade="all", orphanRemoval=true)
     */
    private $entitiesB;

    /**
     * @var \Doctrine\Common\Collections\ArrayCollection
     */
    private $myNotMappedField;

    public function __construct() {
        /*initializes here fields mapped by Doctrine to db*/
        $this->name='';

        $this->initNotMappedFields();
    }

    /**
     * Here I initialize properties not handled by Doctrine
     * @ORM\PostLoad()
     */
    public function initNotMappedFields() {
        $this->myNotMappedField=new \Doctrine\Common\Collections\ArrayCollection();
    }
}

EntityB:

/**
 * EntityB

 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks()

 */
class EntityB {
    /**
     * @var int
     * @ORM\Id
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $name;


    /**
     * @var \Doctrine\Common\Collections\ArrayCollection
     * @ORM\ManyToOne(targetEntity="EntityA", inversedBy="entitiesB")
     */
    private $entityA


    /**
     * @var \Doctrine\Common\Collections\ArrayCollection
     */
    private $myNotMappedField;

    public function __construct() {
        /*initializes here fields mapped by Doctrine to db*/
        $this->name='';

        $this->initNotMappedFields();
    }

    /**
     * Here I initialize properties not handled by Doctrine
     * @ORM\PostLoad()
     */
    public function initNotMappedFields() {
        $this->myNotMappedField=new \Doctrine\Common\Collections\ArrayCollection();
    }
}

Controller:

// this works right:
// EntityA::initNotMappedFields() is properly called
$entityA = $this->getDoctrine()->getManager()->getRepository('AppBundle:EntityA')->findOneById(1); 
// EntityB::initNotMappedFields() is properly called
$entityA->getEntitiesB();

// I want to fetch join EntityB into EntityA, to avoid
// multiple single SQL statements to be executed against the DB
// EntityA::initNotMappedFields() is called
$entityA = $this->getDoctrine()->getManager()->getRepository('AppBundle:EntityA')->createQueryBuilder('entA')
    ->addSelect(['entB'])
    ->andWhere('entA=:id')->setParameter('id', $id)
    ->leftJoin('entA.entitiesB', 'entB')
    ->getQuery()->getOneOrNullResult();

// EntityB::initNotMappedFields() is NOT called
$entityA->getEntitiesB();

What am I missing?

like image 424
ste Avatar asked Oct 21 '25 16:10

ste


1 Answers

This occurs because the first example is going to generate a SQL query to get each individual EntityB and then load it into EntityA. If A has 10 Bs, you'll get 10 additional queries to get each B which call your postLoad() callback on B, because the EntityManager is the one constructing EntityB. See the documentation on the postLoad() event.

What I would do is simply use EntityA to call your initialization on EntityB:

/**
 * Here I initialize properties not handled by Doctrine
 * @ORM\PostLoad()
 */
public function initNotMappedFields()
{
    $this->myNotMappedField = new \Doctrine\Common\Collections\ArrayCollection();

    // the 'if' here is in case you load EntityA without joining EntityB
    // so that you won't cause the extra queries if you don't want EntityB in there
    if ($this->entitiesB) {
        foreach ($this->entitiesB as $entityB) {
            $entityB->initNotMappedFields();
        }
    }
}
like image 96
Jason Roman Avatar answered Oct 23 '25 08:10

Jason Roman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!