What is the best way to force doctrine to load nested associated entities from a query JOIN?
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.
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')
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With