Skip to content

Commit 140ba7a

Browse files
author
abluchet
committed
fix eager loading filter with non-association composite identifier
1 parent ee30666 commit 140ba7a

File tree

3 files changed

+71
-4
lines changed

3 files changed

+71
-4
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGenerator
6969
$queryBuilderClone->andWhere($queryBuilderClone->expr()->in($originAlias, $in->getDQL()));
7070
} else {
7171
// Because Doctrine doesn't support WHERE ( foo, bar ) IN () (https://github.com/doctrine/doctrine2/issues/5238), we are building as many subqueries as they are identifiers
72-
foreach ($classMetadata->identifier as $identifier) {
72+
foreach ($classMetadata->getIdentifier() as $identifier) {
73+
if (!$classMetadata->hasAssociation($identifier)) {
74+
continue;
75+
}
76+
7377
$replacementAlias = $queryNameGenerator->generateJoinAlias($originAlias);
7478
$in = $this->getQueryBuilderWithNewAliases($queryBuilder, $queryNameGenerator, $originAlias, $replacementAlias);
7579
$in->select("IDENTITY($replacementAlias.$identifier)");

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private function hasFetchEagerAssociation(EntityManager $em, ClassMetadataInfo $
9191
{
9292
$checked[] = $classMetadata->name;
9393

94-
foreach ($classMetadata->associationMappings as $mapping) {
94+
foreach ($classMetadata->getAssociationMappings() as $mapping) {
9595
if (ClassMetadataInfo::FETCH_EAGER === $mapping['fetch']) {
9696
return true;
9797
}

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

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,14 @@ public function testCompositeIdentifiers()
246246
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
247247
$resourceMetadataFactoryProphecy->create(CompositeRelation::class)->willReturn(new ResourceMetadata(CompositeRelation::class));
248248

249-
$classMetadata = new ClassMetadataInfo(CompositeRelation::class);
249+
$classMetadataProphecy = $this->prophesize(ClassMetadataInfo::class);
250+
$classMetadataProphecy->getIdentifier()->willReturn(['item', 'label']);
251+
$classMetadataProphecy->getAssociationMappings()->willReturn(['item' => ['fetch' => ClassMetadataInfo::FETCH_EAGER]]);
252+
$classMetadataProphecy->hasAssociation('item')->shouldBeCalled()->willReturn(true);
253+
$classMetadataProphecy->hasAssociation('label')->shouldBeCalled()->willReturn(true);
254+
255+
$classMetadata = $classMetadataProphecy->reveal();
250256
$classMetadata->isIdentifierComposite = true;
251-
$classMetadata->identifier = ['item', 'label'];
252257

253258
$em = $this->prophesize(EntityManager::class);
254259
$em->getExpressionBuilder()->shouldBeCalled()->willReturn(new Expr());
@@ -353,6 +358,64 @@ public function testFetchEagerWithNoForceEager()
353358
$this->assertEquals($this->toDQLString($expected), $qb->getDQL());
354359
}
355360

361+
public function testCompositeIdentifiersWithoutAssociation()
362+
{
363+
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
364+
$resourceMetadataFactoryProphecy->create(CompositeRelation::class)->willReturn(new ResourceMetadata(CompositeRelation::class));
365+
366+
$classMetadataProphecy = $this->prophesize(ClassMetadataInfo::class);
367+
$classMetadataProphecy->getIdentifier()->willReturn(['item', 'label', 'bar']);
368+
$classMetadataProphecy->getAssociationMappings()->willReturn(['item' => ['fetch' => ClassMetadataInfo::FETCH_EAGER]]);
369+
$classMetadataProphecy->hasAssociation('item')->shouldBeCalled()->willReturn(true);
370+
$classMetadataProphecy->hasAssociation('label')->shouldBeCalled()->willReturn(true);
371+
$classMetadataProphecy->hasAssociation('bar')->shouldBeCalled()->willReturn(false);
372+
373+
$classMetadata = $classMetadataProphecy->reveal();
374+
$classMetadata->isIdentifierComposite = true;
375+
376+
$em = $this->prophesize(EntityManager::class);
377+
$em->getExpressionBuilder()->shouldBeCalled()->willReturn(new Expr());
378+
$em->getClassMetadata(CompositeRelation::class)->shouldBeCalled()->willReturn($classMetadata);
379+
380+
$qb = new QueryBuilder($em->reveal());
381+
382+
$qb->select('o')
383+
->from(CompositeRelation::class, 'o')
384+
->innerJoin('o.compositeItem', 'item')
385+
->innerJoin('o.compositeLabel', 'label')
386+
->where('item.field1 = :foo AND o.bar = :bar')
387+
->setParameter('foo', 1)
388+
->setParameter('bar', 2);
389+
390+
$queryNameGenerator = $this->prophesize(QueryNameGeneratorInterface::class);
391+
$queryNameGenerator->generateJoinAlias('item')->shouldBeCalled()->willReturn('item_2');
392+
$queryNameGenerator->generateJoinAlias('label')->shouldBeCalled()->willReturn('label_2');
393+
$queryNameGenerator->generateJoinAlias('o')->shouldBeCalled()->willReturn('o_2');
394+
395+
$filterEagerLoadingExtension = new FilterEagerLoadingExtension($resourceMetadataFactoryProphecy->reveal(), true);
396+
$filterEagerLoadingExtension->applyToCollection($qb, $queryNameGenerator->reveal(), CompositeRelation::class, 'get');
397+
398+
$expected = <<<SQL
399+
SELECT o
400+
FROM ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\CompositeRelation o
401+
INNER JOIN o.compositeItem item
402+
INNER JOIN o.compositeLabel label
403+
WHERE (o.item IN(
404+
SELECT IDENTITY(o_2.item) FROM ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\CompositeRelation o_2
405+
INNER JOIN o_2.compositeItem item_2
406+
INNER JOIN o_2.compositeLabel label_2
407+
WHERE item_2.field1 = :foo AND o_2.bar = :bar
408+
)) AND (o.label IN(
409+
SELECT IDENTITY(o_2.label) FROM ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\CompositeRelation o_2
410+
INNER JOIN o_2.compositeItem item_2
411+
INNER JOIN o_2.compositeLabel label_2
412+
WHERE item_2.field1 = :foo AND o_2.bar = :bar
413+
))
414+
SQL;
415+
416+
$this->assertEquals($this->toDQLString($expected), $qb->getDQL());
417+
}
418+
356419
private function toDQLString(string $dql): string
357420
{
358421
return preg_replace(['/\s+/', '/\(\s/', '/\s\)/'], [' ', '(', ')'], $dql);

0 commit comments

Comments
 (0)