Skip to content

Commit 62834f5

Browse files
authored
Merge pull request #2793 from antograssiot/join-condition-replacement
[2.4] Fixed join condition replacement.
2 parents c4623c3 + 54b5020 commit 62834f5

File tree

4 files changed

+74
-4
lines changed

4 files changed

+74
-4
lines changed

src/Bridge/Doctrine/Orm/Extension/FilterEagerLoadingExtension.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,11 @@ private function getQueryBuilderWithNewAliases(QueryBuilder $queryBuilder, Query
149149
}
150150
$alias = substr($joinString, 0, $pos);
151151
$association = substr($joinString, $pos + 1);
152-
$condition = str_replace($aliases, $replacements, $joinPart->getCondition());
153-
$newAlias = QueryBuilderHelper::addJoinOnce($queryBuilderClone, $queryNameGenerator, $alias, $association, $joinPart->getJoinType(), $joinPart->getConditionType(), $condition, $originAlias);
152+
$newAlias = $queryNameGenerator->generateJoinAlias($association);
154153
$aliases[] = "{$joinPart->getAlias()}.";
155154
$replacements[] = "$newAlias.";
155+
$condition = str_replace($aliases, $replacements, $joinPart->getCondition());
156+
QueryBuilderHelper::addJoinOnce($queryBuilderClone, $queryNameGenerator, $alias, $association, $joinPart->getJoinType(), $joinPart->getConditionType(), $condition, $originAlias, $newAlias);
156157
}
157158

158159
$queryBuilderClone->add('where', str_replace($aliases, $replacements, (string) $wherePart));

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ private function __construct()
3131
/**
3232
* Adds a join to the QueryBuilder if none exists.
3333
*/
34-
public static function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $association, string $joinType = null, string $conditionType = null, string $condition = null, string $originAlias = null): string
34+
public static function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $association, string $joinType = null, string $conditionType = null, string $condition = null, string $originAlias = null, string $newAlias = null): string
3535
{
3636
$join = self::getExistingJoin($queryBuilder, $alias, $association, $originAlias);
3737

3838
if (null !== $join) {
3939
return $join->getAlias();
4040
}
4141

42-
$associationAlias = $queryNameGenerator->generateJoinAlias($association);
42+
$associationAlias = $newAlias ?? $queryNameGenerator->generateJoinAlias($association);
4343
$query = "$alias.$association";
4444

4545
if (Join::LEFT_JOIN === $joinType || QueryChecker::hasLeftJoin($queryBuilder)) {

tests/Bridge/Doctrine/Orm/Extension/FilterEagerLoadingExtensionTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,47 @@ public function testApplyCollectionWithManualJoin()
211211
$this->assertEquals($this->toDQLString($expected), $qb->getDQL());
212212
}
213213

214+
public function testApplyCollectionCorrectlyReplacesJoinCondition()
215+
{
216+
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
217+
$resourceMetadataFactoryProphecy->create(DummyCar::class)->willReturn(new ResourceMetadata(DummyCar::class));
218+
219+
$resourceNameCollectionFactoryProphecy = $this->prophesize(ResourceNameCollectionFactoryInterface::class);
220+
$resourceNameCollectionFactoryProphecy->create()->willReturn(new ResourceNameCollection([DummyTravel::class]));
221+
222+
$em = $this->prophesize(EntityManager::class);
223+
$em->getExpressionBuilder()->shouldBeCalled()->willReturn(new Expr());
224+
$em->getClassMetadata(DummyCar::class)->shouldBeCalled()->willReturn(new ClassMetadataInfo(DummyCar::class));
225+
226+
$qb = new QueryBuilder($em->reveal());
227+
228+
$qb->select('o')
229+
->from(DummyCar::class, 'o')
230+
->leftJoin('o.colors', 'colors', 'ON', 'o.id = colors.car AND colors.id IN (1,2,3)')
231+
->where('o.colors = :foo')
232+
->setParameter('foo', 1);
233+
234+
$queryNameGenerator = $this->prophesize(QueryNameGeneratorInterface::class);
235+
$queryNameGenerator->generateJoinAlias('colors')->shouldBeCalled()->willReturn('colors_2');
236+
$queryNameGenerator->generateJoinAlias('o')->shouldBeCalled()->willReturn('o_2');
237+
238+
$filterEagerLoadingExtension = new FilterEagerLoadingExtension($resourceMetadataFactoryProphecy->reveal(), true, new ResourceClassResolver($resourceNameCollectionFactoryProphecy->reveal()));
239+
$filterEagerLoadingExtension->applyToCollection($qb, $queryNameGenerator->reveal(), DummyCar::class, 'get');
240+
241+
$expected = <<<'SQL'
242+
SELECT o
243+
FROM ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyCar o
244+
LEFT JOIN o.colors colors ON o.id = colors.car AND colors.id IN (1,2,3)
245+
WHERE o IN(
246+
SELECT o_2 FROM ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyCar o_2
247+
LEFT JOIN o_2.colors colors_2 ON o_2.id = colors_2.car AND colors_2.id IN (1,2,3)
248+
WHERE o_2.colors = :foo
249+
)
250+
SQL;
251+
252+
$this->assertEquals($this->toDQLString($expected), $qb->getDQL());
253+
}
254+
214255
/**
215256
* https://github.com/api-platform/core/issues/1021.
216257
*/

tests/Bridge/Doctrine/Orm/Util/QueryBuilderHelperTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Doctrine\ORM\Mapping\ClassMetadata;
2323
use Doctrine\ORM\QueryBuilder;
2424
use PHPUnit\Framework\TestCase;
25+
use Prophecy\Argument;
2526

2627
class QueryBuilderHelperTest extends TestCase
2728
{
@@ -53,6 +54,33 @@ public function testAddJoinOnce(?string $originAliasForJoinOnce, string $expecte
5354
$queryBuilder->getDQLPart('join')[$originAliasForJoinOnce ?? 'f'][0]->getAlias());
5455
}
5556

57+
/**
58+
* @dataProvider provideAddJoinOnce
59+
*/
60+
public function testAddJoinOnceWithSpecifiedNewAlias()
61+
{
62+
$queryBuilder = new QueryBuilder($this->prophesize(EntityManagerInterface::class)->reveal());
63+
$queryBuilder->from('foo', 'f');
64+
65+
$queryNameGenerator = $this->prophesize(QueryNameGeneratorInterface::class);
66+
$queryNameGenerator->generateJoinAlias(Argument::any())->shouldNotbeCalled();
67+
68+
QueryBuilderHelper::addJoinOnce(
69+
$queryBuilder,
70+
$queryNameGenerator->reveal(),
71+
'f',
72+
'bar',
73+
null,
74+
null,
75+
null,
76+
null,
77+
'f_8'
78+
);
79+
80+
$this->assertSame('f_8',
81+
$queryBuilder->getDQLPart('join')['f'][0]->getAlias());
82+
}
83+
5684
public function testGetEntityClassByAliasWithJoinByAssociation(): void
5785
{
5886
$dummyMetadata = new ClassMetadata(Dummy::class);

0 commit comments

Comments
 (0)