-
-
Notifications
You must be signed in to change notification settings - Fork 921
[RFR] Issues/1246 #1324
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
[RFR] Issues/1246 #1324
Conversation
$field = $order; | ||
$order = 'ASC'; | ||
} | ||
$queryBuilder->addOrderBy('o.'.$field, $order); | ||
if (false === strstr($field, '.')) { |
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.
Can't you use strpos
and store the result in a variable to use it if it doesn't return false
? It will remove one function call.
// Ensure query join is done on association | ||
$associationName = substr($field, 0, $pos); | ||
if (!in_array($associationName, $queryBuilder->getRootAliases(), true)) { | ||
$queryBuilder->join('o.'.$associationName, $associationName); |
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.
No conflicts with src/Bridge/Doctrine/Orm/Extension/FilterEagerLoadingExtension.php#L117-L124 here (extensions priorities)?
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.
@dunglas WDYT?
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.
Can you use the JoinOnce thing? This may lead to multiple joins indeed.
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.
@soyuka What do you mean? Do you have any example please?
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.
Maybe this:
core/src/Bridge/Doctrine/Orm/Filter/AbstractFilter.php
Lines 382 to 401 in 0e1af77
protected function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $association): string | |
{ | |
$join = $this->getExistingJoin($queryBuilder, $alias, $association); | |
if (null === $join) { | |
$associationAlias = $queryNameGenerator->generateJoinAlias($association); | |
if (true === QueryChecker::hasLeftJoin($queryBuilder)) { | |
$queryBuilder | |
->leftJoin(sprintf('%s.%s', $alias, $association), $associationAlias); | |
} else { | |
$queryBuilder | |
->innerJoin(sprintf('%s.%s', $alias, $association), $associationAlias); | |
} | |
} else { | |
$associationAlias = $join->getAlias(); | |
} | |
return $associationAlias; | |
} |
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.
What do you think about a QueryBuilderHelper, which can be shared with filters & extensions?
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.
Would be nice yeah, maybe we can do this in another PR!
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 better think it could be done in this PR as its totally related to it. I could create this QueryBuilderHelper and move addJoinsForNestedProperty
, getExistingJoin
& addJoinOnce
methods in it.
But I'm not sure it's the best approach, I think it should be better to call these methods on the QueryBuilder object directly instead of using an external service which modify the query builder object. Something like $queryBuilder->addJoinOnce
instead of QueryBuilderHelper::addJoinOnce($queryBuilder)
, wdyt @soyuka?
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.
The helper (as long as it is marked @internal
) looks good to me. The QueryBuilder is provided by Doctrine, it's not really wanted to modify it.
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'd keep static helpers instead of extending the QueryBuilder.
* | ||
* @return string the new association alias | ||
*/ | ||
public static function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $association, ?string $joinType = null, ?string $conditionType = null, ?string $condition = null): string |
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.
You can't use the ?string
syntax because we ensure compatibility with PHP 7.0 (this syntax was introduced in 7.1).
/** @var Join $joinPart */ | ||
$joinString = str_replace($aliases, $replacements, $joinPart->getJoin()); | ||
$alias = substr($joinString, 0, strpos($joinString, '.')); | ||
$association = substr($joinString, strpos($joinString, '.')+1); |
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.
can we avoid multiple calls on strpos?
Do you think it's possible that strpos returns false
here? Maybe we kinda need a test?
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.
@soyuka As it's from a cloned query builder, I don't think it's even possible that strpos
could return false
. It would mean that the original query builder is invalid, which report the bug to doctrine.
@@ -42,15 +41,13 @@ class Foo | |||
* @var string The foo name | |||
* | |||
* @ORM\Column | |||
* @Assert\NotBlank |
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.
Really not needed? I'd have think those are for a given behat test, if not np just remove them though it's not part of the fix (or is it?).
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.
Originally, I created this Foo
entity for my tests. It was copied/pasted from another one. But I think the assertion is useless on this entity as I only create the objects by myself without validation.
This modification (in this PR) is a clean :-)
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.
It would be nice to compare SQL queries before and after these changes to verify that they are identical. These ones seem to me very sensitive...
* | ||
* @internal | ||
*/ | ||
final class QueryBuilderHelper |
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.
Can you add a private constructor?
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.
Why for?
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.
Because he likes private constructors :P.
Nah just that it's a convention when classes have no instances (ie are never called with new
because they only have statics).
@meyerbaptiste on which class: FilterEagerLoadingExtension or QueryBuilderHelper? |
@meyerbaptiste He didn't changed the tests and we do test queries there. |
{ | ||
$parts = $queryBuilder->getDQLPart('join'); | ||
|
||
if (!isset($parts['o'])) { |
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.
Maybe we could put this o
alias in some constant somewhere (in another PR).
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.
It could be easily retried by $queryBuilder->getRootAliases()[0]
. Should I replace it here?
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.
don't IMO it's fine like this. It's just that we have a lot of these 'o'
alias hard-coded in the bridge.
Would you mind fixing phpstan? |
@soyuka I don't understand where the error comes from in my code:
|
Just remove this line: |
$queryNameGenerator->generateJoinAlias('item')->shouldBeCalled()->willReturn('item_2'); | ||
$queryNameGenerator->generateJoinAlias('label')->shouldBeCalled()->willReturn('label_2'); | ||
$queryNameGenerator->generateJoinAlias('compositeItem')->shouldBeCalled()->willReturn('item_2'); | ||
$queryNameGenerator->generateJoinAlias('compositeLabel')->shouldBeCalled()->willReturn('label_2'); |
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 change is wrong, item
and label
are correct I think (at least according to failing tests).
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.
Nop, cause this call is made through QueryBuilderHelper::addJoinOnce from association name.
I don't understand why travis is failing…on a non-existing unit test 😮 (failing method testCompositeIdentifiersWithoutAssociation
does not exist in FilterEagerLoadingExtensionTest
)
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 try to squash my changes to force travis to rebuild its environment
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 added this recently maybe a bad rebase?
https://github.com/api-platform/core/blob/d18ec0da1ac52f92ca755ca275cd0853d0972878/tests/Bridge/Doctrine/Orm/Extension/FilterEagerLoadingExtensionTest.php
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.
Rebase missing, I fixed it, thanks @soyuka
👍 waiting for some more review but this LGTM! |
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.
Some minor changes. 👍 otherwise
/** | ||
* Adds a join to the queryBuilder if none exists. | ||
* | ||
* @param QueryBuilder $queryBuilder |
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.
You can remove most of these @param
.
*/ | ||
public static function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $association, $joinType = null, $conditionType = null, $condition = null): string | ||
{ | ||
$join = self::getExistingJoin($queryBuilder, $alias, $association); |
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.
What about:
if (null !== $join) {
return $join->getAlias();
}
$associationAlias = $queryNameGenerator->generateJoinAlias($association);
$query = "$alias.$association";
if (Join::LEFT_JOIN === $joinType || QueryChecker::hasLeftJoin($queryBuilder)) {
$queryBuilder->leftJoin($query, $associationAlias, $conditionType, $condition);
} else {
$queryBuilder->innerJoin($query, $associationAlias, $conditionType, $condition);
}
return $associationAlias;
To reduce the complexity of this method.
/** | ||
* Get the existing join from queryBuilder DQL parts. | ||
* | ||
* @param QueryBuilder $queryBuilder |
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.
All @param
can be removed
Thanks @vincentchalamon! |
Uh oh!
There was an error while loading. Please reload this page.