[PR #11870] Make generated QueryComponents publicly available in ParserResult #13304

Closed
opened 2026-01-22 16:16:47 +01:00 by admin · 0 comments
Owner

Original Pull Request: https://github.com/doctrine/orm/pull/11870

State: closed
Merged: No


The Parser turns A DQL query in a ParserResult. This parser generates all the QueryComponents, which hold all the mappings calculated from the DQL query. But in the end these calculated components are never consultable. This is actually very useful information but you can't access it anywhere. So that really is a pity!

A scenario where access to this generated data becomes very useful is when you have a QueryBuilder with subQueries which are injected with DQL.
Let's say you have a dynamic query builder that goes through a lot of code and might add subqueries along the way.

// $subqueryBuilder holds potential ToMany joins

if ($forSomeReason) {
    $queryBuilder->andWhere($qb->expr()->in('n.user', $subqueryBuilder->getDQL()))
}

In the end just before executing the query I want to check my QueryBuilder if it has at least one ...toMany relation before I add

  • ->distinct() (if no aggregate selects found)
  • OR ->addGroupBy('rootalias.id') (depending if the select has aggregates or not)

I only want to add distinct or groupBy if it is really necessary because it is a performance hit. There is no need to add it if we don't have toMany relations in the final query.

Because we have DQL injected in our query we lose the original "$subqueryBuilder" join mapping information in the final $queryBuilder

So By adding the generated QueryComponents to the ParserResult all the oh-so-valuable mapping information with all the join types becomes available.

To solve this problem in my current project I made this solution with a ReflectionClass to access the private generated "queryComponents"

$parser = new Parser($qb->getQuery());
$parser->parse();
$reflectionParser = new \ReflectionClass($parser);
$queryComponentsProp = $reflectionParser->getProperty('queryComponents');
$queryComponentsProp->setAccessible(true);
$queryComponents = $queryComponentsProp->getValue($parser);

But it would be nice that we all could just do $parserResult->getQueryComponents(), and then we can check if there is at least one ...toMany relation with

$parser = new Parser($qb->getQuery());
$parserResult = $parser->parse();

$hasToManyJoins = false;
foreach ($parserResult->getQueryComponents() as $queryComponent) {
    $relationType = $queryComponent['relation']['type'] ?? null;
            
    if (in_array($relationType, [
        ClassMetadataInfo::ONE_TO_MANY,
        ClassMetadataInfo::MANY_TO_MANY,
    ])) {
        $hasToManyJoins = true;
        break;
    }
}
**Original Pull Request:** https://github.com/doctrine/orm/pull/11870 **State:** closed **Merged:** No --- The Parser turns A DQL query in a ParserResult. This parser generates all the **QueryComponents**, which hold all the mappings calculated from the DQL query. But in the end these calculated components are never consultable. This is actually very useful information but you can't access it anywhere. So that really is a pity! A scenario where access to this generated data becomes very useful is when you have a QueryBuilder with subQueries which are injected with DQL. Let's say you have a dynamic query builder that goes through a lot of code and might add subqueries along the way. ```` // $subqueryBuilder holds potential ToMany joins if ($forSomeReason) { $queryBuilder->andWhere($qb->expr()->in('n.user', $subqueryBuilder->getDQL())) } ```` In the end just before executing the query I want to check my QueryBuilder if it has at least one ...toMany relation before I add * **->distinct()** (if no aggregate selects found) * OR **->addGroupBy('rootalias.id')** (depending if the select has aggregates or not) I only want to add distinct or groupBy if it is really necessary because it is a **performance hit**. There is no need to add it if we don't have toMany relations in the final query. Because we have DQL injected in our query we lose the original "$subqueryBuilder" join mapping information in the final $queryBuilder So By adding the generated QueryComponents to the ParserResult all the oh-so-valuable mapping information with all the join types becomes available. To solve this problem in my current project I made this solution with a ReflectionClass to access the private generated "queryComponents" ```` $parser = new Parser($qb->getQuery()); $parser->parse(); $reflectionParser = new \ReflectionClass($parser); $queryComponentsProp = $reflectionParser->getProperty('queryComponents'); $queryComponentsProp->setAccessible(true); $queryComponents = $queryComponentsProp->getValue($parser); ```` But it would be nice that we all could just do $parserResult->getQueryComponents(), and then we can check if there is at least one ...toMany relation with ```` $parser = new Parser($qb->getQuery()); $parserResult = $parser->parse(); $hasToManyJoins = false; foreach ($parserResult->getQueryComponents() as $queryComponent) { $relationType = $queryComponent['relation']['type'] ?? null; if (in_array($relationType, [ ClassMetadataInfo::ONE_TO_MANY, ClassMetadataInfo::MANY_TO_MANY, ])) { $hasToManyJoins = true; break; } } ````
admin added the pull-request label 2026-01-22 16:16:47 +01:00
admin closed this issue 2026-01-22 16:16:47 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#13304