New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix duplicated eager loading joins #3525
Fix duplicated eager loading joins #3525
Conversation
julienfalque
commented
Apr 24, 2020
•
edited
edited
Q | A |
---|---|
Bug fix? | yes |
New feature? | no |
BC breaks? | no |
Deprecations? | no |
Tickets | #3487 |
License | MIT |
Doc PR | - |
This looks good according to the tests, do you think that there's a way to add a non-regression test? |
707d408
to
35c9ce6
Compare
I was not able to reproduce the issue I mentioned in #3487 yet, I need a more complex filter I think. For now, changing the priority results in: SELECT o, relatedDummy_a1
FROM ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy o
- LEFT JOIN o.relatedDummy relatedDummy_a1
- LEFT JOIN relatedDummy_a1.thirdLevel thirdLevel_a2
+ INNER JOIN o.relatedDummy relatedDummy_a1
+ INNER JOIN relatedDummy_a1.thirdLevel thirdLevel_a2
WHERE o IN(
SELECT o_a3
FROM ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy o_a3
- LEFT JOIN o_a3.relatedDummy relatedDummy_a4
- LEFT JOIN relatedDummy_a4.thirdLevel thirdLevel_a5
+ INNER JOIN o_a3.relatedDummy relatedDummy_a4
+ INNER JOIN relatedDummy_a4.thirdLevel thirdLevel_a5
WHERE thirdLevel_a5.level = :level_p1
)
ORDER BY o.id ASC which looks ok. What do you think about the current approach? |
35c9ce6
to
91fbc79
Compare
Ok I reproduced the issue (see method
The nested query in the first
|
3bb73a9
to
57c8e3c
Compare
Behat failures seem unrelated. |
57c8e3c
to
54e5283
Compare
a933a88
to
18966be
Compare
18966be
to
e5f3125
Compare
Hello @julienfalque. Sorry for the time we took to review your PR. |
f6d084b
to
977ad06
Compare
@alanpoulain Thank you for tackling this. To be honest I don't remember exactly. I think this specific test case was the only one that actually reproduced #3487 (see #3525 (comment)) so it should be kept. I'll have a closer look as soon as I can. |
aaf901d
to
b0e827b
Compare
OK I've added it again then. |
@soyuka could you take a look at it? |
b0e827b
to
a170059
Compare
I cannot approve my own PR but your changes look 👍, thanks! |
a170059
to
ae973d3
Compare
Thank you 🙂 |
Thank you @alanpoulain @soyuka. |
@@ -131,7 +131,8 @@ | |||
<argument type="service" id="serializer.mapping.class_metadata_factory" /> | |||
|
|||
<tag name="api_platform.doctrine.orm.query_extension.item" priority="-8" /> | |||
<tag name="api_platform.doctrine.orm.query_extension.collection" priority="-8" /> | |||
<!-- After filter_eager_loading --> | |||
<tag name="api_platform.doctrine.orm.query_extension.collection" priority="-18" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this priority change breaks filters that rely on existing joins from the eager loading extension because now the eager loading runs after the filters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exmaple filter that now breaks.
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
{
if ('searchTerm' !== $property || !in_array($resourceClass, [User::class, Employment::class]))
return;
$alias = $rootAlias = $queryBuilder->getRootAliases()[0];
if (User::class !== $resourceClass) {
$joins = $queryBuilder->getDQLPart("join");
if (array_key_exists($rootAlias, $joins)) {
/** @var \Doctrine\ORM\Query\Expr\Join $join */
foreach ($joins[$rootAlias] as $join) {
if (sprintf('%s.user', $rootAlias) === $join->getJoin())
$alias = $join->getAlias();
}
}
}
$searchParam = $queryNameGenerator->generateParameterName('search');
$conditions = [];
$conditions[] = sprintf('%1$s.firstname LIKE :%2$s', $alias, $searchParam);
$conditions[] = sprintf('%1$s.lastname LIKE :%2$s', $alias, $searchParam);
$conditions[] = sprintf('CONCAT(%1$s.firstname, \' \', %1$s.lastname) LIKE :%2$s', $alias, $searchParam);
$conditions[] = sprintf('%1$s.email LIKE :%2$s', $alias, $searchParam);
$queryBuilder->andWhere(implode(' OR ', $conditions))
->setParameter($searchParam, $value . "%");
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could workaround this issue by adding the join explicitly in the filter, but now we join the relation two times, one by the filter and another by the eager loading extension.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could workaround this issue by adding the join explicitly in the filter, but now we join the relation two times, one by the filter and another by the eager loading extension.
If the extension works correctly, the existing join is reused then.
@julienfalque do you think it's possible to solve the issue without changing the priority?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll have to check again, but as I remember this was required to prevent duplicating joins as described in #3487.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just had a look: changing this priority is required to get EagerLoadingExtension
applied after FilterEagerLoadingExtension
to fix #3487. I don't think FilterEagerLoadingExtension
's priority can be changed (it has to be applied after all filters).
we could workaround this issue by adding the join explicitly in the filter, but now we join the relation two times, one by the filter and another by the eager loading extension.
If I remember correctly, the changes in this PR make EagerLoadingExtension
able to rely on existing joins when possible instead of always adding its own. @zolex Are you sure you have duplicated joins if you add one in your filter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just assumed it because in earlier Versions it happened. I will check the resulting queries tomorrow and let you know.