Skip to content

Commit 54e5283

Browse files
committed
Fix duplicated eager loading joins
1 parent 6120226 commit 54e5283

File tree

6 files changed

+420
-11
lines changed

6 files changed

+420
-11
lines changed

phpstan.neon.dist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ parameters:
109109
-
110110
message: '#Cannot assign new offset to string\.#'
111111
path: src/JsonSchema/SchemaFactory.php
112+
# https://github.com/phpstan/phpstan-doctrine/pull/135
113+
-
114+
message: '#^Method ApiPlatform\\Core\\Tests\\Bridge\\Doctrine\\Orm\\EntityManager::getRepository\(\) should return Doctrine\\Persistence\\ObjectRepository<T> but returns Doctrine\\Persistence\\ObjectRepository<object>\.$#'
115+
path: tests/Bridge/Doctrine/Orm/CollectionDataProviderIntegrationTest.php
112116

113117
# Expected, due to optional interfaces
114118
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Extension\\QueryCollectionExtensionInterface::applyToCollection\(\) invoked with 5 parameters, 3-4 required\.#'

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

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace ApiPlatform\Core\Bridge\Doctrine\Orm\Extension;
1515

1616
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\EagerLoadingTrait;
17+
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryBuilderHelper;
1718
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
1819
use ApiPlatform\Core\Exception\InvalidArgumentException;
1920
use ApiPlatform\Core\Exception\PropertyNotFoundException;
@@ -24,6 +25,7 @@
2425
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
2526
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
2627
use Doctrine\ORM\Mapping\ClassMetadataInfo;
28+
use Doctrine\ORM\Query\Expr\Join;
2729
use Doctrine\ORM\QueryBuilder;
2830
use Symfony\Component\HttpFoundation\RequestStack;
2931
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
@@ -190,16 +192,20 @@ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInt
190192
continue;
191193
}
192194

193-
$isNullable = $mapping['joinColumns'][0]['nullable'] ?? true;
194-
if (false !== $wasLeftJoin || true === $isNullable) {
195-
$method = 'leftJoin';
195+
$existingJoin = QueryBuilderHelper::getExistingJoin($queryBuilder, $parentAlias, $association);
196+
197+
if (null !== $existingJoin) {
198+
$associationAlias = $existingJoin->getAlias();
199+
$isLeftJoin = Join::LEFT_JOIN === $existingJoin->getJoinType();
196200
} else {
197-
$method = 'innerJoin';
198-
}
201+
$isNullable = $mapping['joinColumns'][0]['nullable'] ?? true;
202+
$isLeftJoin = false !== $wasLeftJoin || true === $isNullable;
203+
$method = $isLeftJoin ? 'leftJoin' : 'innerJoin';
199204

200-
$associationAlias = $queryNameGenerator->generateJoinAlias($association);
201-
$queryBuilder->{$method}(sprintf('%s.%s', $parentAlias, $association), $associationAlias);
202-
++$joinCount;
205+
$associationAlias = $queryNameGenerator->generateJoinAlias($association);
206+
$queryBuilder->{$method}(sprintf('%s.%s', $parentAlias, $association), $associationAlias);
207+
++$joinCount;
208+
}
203209

204210
if (true === $fetchPartial) {
205211
try {
@@ -230,7 +236,7 @@ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInt
230236
}
231237
}
232238

233-
$this->joinRelations($queryBuilder, $queryNameGenerator, $mapping['targetEntity'], $forceEager, $fetchPartial, $associationAlias, $options, $normalizationContext, 'leftJoin' === $method, $joinCount, $currentDepth);
239+
$this->joinRelations($queryBuilder, $queryNameGenerator, $mapping['targetEntity'], $forceEager, $fetchPartial, $associationAlias, $options, $normalizationContext, $isLeftJoin, $joinCount, $currentDepth);
234240
}
235241
}
236242

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public static function traverseJoins(string $alias, QueryBuilder $queryBuilder,
160160
/**
161161
* Gets the existing join from QueryBuilder DQL parts.
162162
*/
163-
private static function getExistingJoin(QueryBuilder $queryBuilder, string $alias, string $association, string $originAlias = null): ?Join
163+
public static function getExistingJoin(QueryBuilder $queryBuilder, string $alias, string $association, string $originAlias = null): ?Join
164164
{
165165
$parts = $queryBuilder->getDQLPart('join');
166166
$rootAlias = $originAlias ?? $queryBuilder->getRootAliases()[0];

src/Bridge/Symfony/Bundle/Resources/config/doctrine_orm.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@
131131
<argument type="service" id="serializer.mapping.class_metadata_factory" />
132132

133133
<tag name="api_platform.doctrine.orm.query_extension.item" priority="-8" />
134-
<tag name="api_platform.doctrine.orm.query_extension.collection" priority="-8" />
134+
<tag name="api_platform.doctrine.orm.query_extension.collection" priority="-18" />
135135
</service>
136136
<service id="ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\EagerLoadingExtension" alias="api_platform.doctrine.orm.query_extension.eager_loading" />
137137

0 commit comments

Comments
 (0)