Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force Doctrine to Load Associated Entities in JOIN

Question

What is the best way to force doctrine to load nested associated entities from a query JOIN?

Details

I have multiple entities (view, contentType, version, settings), with three levels of associations:

view               (primary entity)
    contentType    (ManyToOne joined on view.contentTypeId = contentType.id)
    version        (OneToMany joined on version.viewId = view.id)
        settings   (OneToOne joined on settings.versionId = version.id)

I have a list that queries all the versions (not view) to get the most recently modified version. But I access properties from view and contentType and settings. I access contentType through view (so it's two levels deep). When I reference these properties in a loop, I do not want Doctrine Lazy Loading each value with N queries for each loop.

So I join the entity in the query, and select all fields.

$versionRepo = $em->getRepository('GutensiteCmsBundle:View\ViewVersion');
$queryBuilder = $versionRepo->createQueryBuilder('e')
    ->join('e.view', 'view')
    ->addSelect('view')
    ->join('view.contentType', 'contentType')
    ->addSelect('contentType')
    ->join('e.settings', 'settings')
    ->where('e.siteId = :siteid')->setParameter('siteId', 1)
    ->setMaxLimit(25);

The first level links ARE fetched properly, e.g. version.view. However, doctrine handles the version.settings like a lazy load, and I'll have 25 additional queries where it loads each individual value.

So even though I join settings it is still lazy loading.

like image 696
Chadwick Meyer Avatar asked Nov 21 '14 23:11

Chadwick Meyer


1 Answers

If you want to avoid Lazy Loading (recommended for loops), Doctrine suggest that you JOIN an entity at the time of the query. But you also have to add the entity to the SELECT statement, e.g.

->join('e.settings', 'settings')
->addSelect('settings')

From the Doctrine Docs on Join

A SELECT query can contain joins. There are 2 types of JOINs: “Regular” Joins and “Fetch” Joins.

Regular Joins: Used to limit the results and/or compute aggregate values.

Fetch Joins: In addition to the uses of regular joins: Used to fetch related entities and include them in the hydrated result of a query.

There is no special DQL keyword that distinguishes a regular join from a fetch join. A join (be it an inner or outer join) becomes a “fetch join” as soon as fields of the joined entity appear in the SELECT part of the DQL query outside of an aggregate function. Otherwise its a “regular join”.

Doctrine also suggest that you can use Query Hints to "hydrate objects although not all their columns are fetched."

$query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);

This could be something to look into if you don't want to join.

like image 79
Chadwick Meyer Avatar answered Nov 02 '22 04:11

Chadwick Meyer