Skip to content

Commit e114c46

Browse files
author
abluchet
committed
specify left join for filter associations
1 parent 84a402c commit e114c46

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

src/Bridge/Doctrine/Orm/Filter/AbstractFilter.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace ApiPlatform\Core\Bridge\Doctrine\Orm\Filter;
1515

16+
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryChecker;
1617
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
1718
use ApiPlatform\Core\Exception\InvalidArgumentException;
1819
use ApiPlatform\Core\Util\RequestParser;
@@ -287,8 +288,14 @@ protected function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGeneratorInt
287288

288289
if (null === $join) {
289290
$associationAlias = $queryNameGenerator->generateJoinAlias($association);
290-
$queryBuilder
291-
->join(sprintf('%s.%s', $alias, $association), $associationAlias);
291+
292+
if (true === QueryChecker::hasLeftJoin($queryBuilder)) {
293+
$queryBuilder
294+
->leftJoin(sprintf('%s.%s', $alias, $association), $associationAlias);
295+
} else {
296+
$queryBuilder
297+
->innerJoin(sprintf('%s.%s', $alias, $association), $associationAlias);
298+
}
292299
} else {
293300
$associationAlias = $join->getAlias();
294301
}

src/Bridge/Doctrine/Orm/Util/QueryChecker.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use Doctrine\Common\Persistence\ManagerRegistry;
1717
use Doctrine\ORM\Mapping\ClassMetadata;
18+
use Doctrine\ORM\Query\Expr\Join;
1819
use Doctrine\ORM\QueryBuilder;
1920

2021
/**
@@ -155,4 +156,24 @@ public static function hasOrderByOnToManyJoin(QueryBuilder $queryBuilder, Manage
155156

156157
return false;
157158
}
159+
160+
/**
161+
* Determines whether the query builder already has a left join.
162+
*
163+
* @param QueryBuilder $queryBuilder
164+
*
165+
* @return bool
166+
*/
167+
public static function hasLeftJoin(QueryBuilder $queryBuilder): bool
168+
{
169+
foreach ($queryBuilder->getDQLPart('join') as $dqlParts) {
170+
foreach ($dqlParts as $dqlPart) {
171+
if (Join::LEFT_JOIN === $dqlPart->getJoinType()) {
172+
return true;
173+
}
174+
}
175+
}
176+
177+
return false;
178+
}
158179
}

tests/Bridge/Doctrine/Orm/Filter/SearchFilterTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,4 +767,27 @@ public function testTripleJoin()
767767
$expected = strtolower(sprintf('SELECT o FROM %s o inner join o.relatedDummy relateddummy_a1 inner join relateddummy_a1.thirdLevel thirdLevel_a1 WHERE relateddummy_a1.symfony = :symfony_p1 and thirdLevel_a1.level = :level_p2', Dummy::class));
768768
$this->assertEquals($actual, $expected);
769769
}
770+
771+
public function testJoinLeft()
772+
{
773+
$request = Request::create('/api/dummies', 'GET', ['relatedDummy.symfony' => 'foo', 'relatedDummy.thirdLevel.level' => 'bar']);
774+
$requestStack = new RequestStack();
775+
$requestStack->push($request);
776+
$queryBuilder = $this->repository->createQueryBuilder('o');
777+
$queryBuilder->leftJoin('o.relatedDummy', 'relateddummy_a1');
778+
779+
$filter = new SearchFilter(
780+
$this->managerRegistry,
781+
$requestStack,
782+
$this->iriConverter,
783+
$this->propertyAccessor,
784+
null,
785+
['relatedDummy.symfony' => null, 'relatedDummy.thirdLevel.level' => null]
786+
);
787+
788+
$filter->apply($queryBuilder, new QueryNameGenerator(), $this->resourceClass, 'op');
789+
$actual = strtolower($queryBuilder->getQuery()->getDQL());
790+
$expected = strtolower(sprintf('SELECT o FROM %s o left join o.relatedDummy relateddummy_a1 left join relateddummy_a1.thirdLevel thirdLevel_a1 WHERE relateddummy_a1.symfony = :symfony_p1 and thirdLevel_a1.level = :level_p2', Dummy::class));
791+
$this->assertEquals($actual, $expected);
792+
}
770793
}

0 commit comments

Comments
 (0)