Skip to content

Commit 6b5df95

Browse files
skalrayiJannik Wettengel
and
Jannik Wettengel
authored
fix(doctrine): odm order filter should use a left join on nullable fields (#5911)
Co-authored-by: Jannik Wettengel <[email protected]>
1 parent 13aa25c commit 6b5df95

File tree

7 files changed

+54
-7
lines changed

7 files changed

+54
-7
lines changed

src/Doctrine/Odm/Extension/OrderExtension.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public function applyToCollection(Builder $aggregationBuilder, string $resourceC
6464
}
6565

6666
if ($this->isPropertyNested($field, $resourceClass)) {
67-
[$field] = $this->addLookupsForNestedProperty($field, $aggregationBuilder, $resourceClass);
67+
[$field] = $this->addLookupsForNestedProperty($field, $aggregationBuilder, $resourceClass, true);
6868
}
6969
$aggregationBuilder->sort(
7070
$context['mongodb_odm_sort_fields'] = ($context['mongodb_odm_sort_fields'] ?? []) + [$field => $order]

src/Doctrine/Odm/Filter/OrderFilter.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ protected function filterProperty(string $property, $direction, Builder $aggrega
253253
$matchField = $property;
254254

255255
if ($this->isPropertyNested($property, $resourceClass)) {
256-
[$matchField] = $this->addLookupsForNestedProperty($property, $aggregationBuilder, $resourceClass);
256+
[$matchField] = $this->addLookupsForNestedProperty($property, $aggregationBuilder, $resourceClass, true);
257257
}
258258

259259
$aggregationBuilder->sort(

src/Doctrine/Odm/PropertyHelperTrait.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ protected function getClassMetadata(string $resourceClass): ClassMetadata
6060
* the second element is the $field name
6161
* the third element is the $associations array
6262
*/
63-
protected function addLookupsForNestedProperty(string $property, Builder $aggregationBuilder, string $resourceClass): array
63+
protected function addLookupsForNestedProperty(string $property, Builder $aggregationBuilder, string $resourceClass, bool $preserveNullAndEmptyArrays = false): array
6464
{
6565
$propertyParts = $this->splitPropertyParts($property, $resourceClass);
6666
$alias = '';
@@ -98,7 +98,8 @@ protected function addLookupsForNestedProperty(string $property, Builder $aggreg
9898
->localField($isOwningSide ? $localField : '_id')
9999
->foreignField($isOwningSide ? '_id' : $referenceMapping['mappedBy'])
100100
->alias($alias);
101-
$aggregationBuilder->unwind("\$$alias");
101+
$aggregationBuilder->unwind("\$$alias")
102+
->preserveNullAndEmptyArrays($preserveNullAndEmptyArrays);
102103

103104
// association.property => association_lkup.property
104105
$property = substr_replace($property, $propertyAlias, strpos($property, (string) $association), \strlen((string) $association));

tests/Doctrine/Common/Filter/OrderFilterTestTrait.php

+8
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,14 @@ private static function provideApplyTestArguments(): array
325325
],
326326
],
327327
],
328+
'nullable field in relation will be a LEFT JOIN' => [
329+
[
330+
'relatedDummy.name' => null,
331+
],
332+
[
333+
'order' => ['relatedDummy.name' => 'ASC'],
334+
],
335+
],
328336
];
329337
}
330338
}

tests/Doctrine/Odm/Extension/OrderExtensionTest.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Doctrine\ODM\MongoDB\Aggregation\Builder;
2020
use Doctrine\ODM\MongoDB\Aggregation\Stage\Lookup;
2121
use Doctrine\ODM\MongoDB\Aggregation\Stage\Sort;
22+
use Doctrine\ODM\MongoDB\Aggregation\Stage\Unwind;
2223
use Doctrine\ODM\MongoDB\DocumentManager;
2324
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
2425
use Doctrine\Persistence\ManagerRegistry;
@@ -128,7 +129,9 @@ public function testApplyToCollectionWithOrderOverriddenWithAssociation(): void
128129
$lookupProphecy->foreignField('_id')->shouldBeCalled()->willReturn($lookupProphecy);
129130
$lookupProphecy->alias('author_lkup')->shouldBeCalled();
130131
$aggregationBuilderProphecy->lookup(Dummy::class)->shouldBeCalled()->willReturn($lookupProphecy->reveal());
131-
$aggregationBuilderProphecy->unwind('$author_lkup')->shouldBeCalled();
132+
$unwindProphecy = $this->prophesize(Unwind::class);
133+
$unwindProphecy->preserveNullAndEmptyArrays(true)->shouldBeCalled();
134+
$aggregationBuilderProphecy->unwind('$author_lkup')->shouldBeCalled()->willReturn($unwindProphecy->reveal());
132135
$aggregationBuilderProphecy->getStage(0)->willThrow(new \OutOfRangeException('message'));
133136
$aggregationBuilderProphecy->sort(['author_lkup.name' => 'ASC'])->shouldBeCalled();
134137

tests/Doctrine/Odm/Filter/OrderFilterTest.php

+32-2
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,10 @@ public static function provideApplyTestData(): array
343343
],
344344
],
345345
[
346-
'$unwind' => '$relatedDummy_lkup',
346+
'$unwind' => [
347+
'path' => '$relatedDummy_lkup',
348+
'preserveNullAndEmptyArrays' => true,
349+
],
347350
],
348351
[
349352
'$sort' => [
@@ -514,7 +517,10 @@ public static function provideApplyTestData(): array
514517
],
515518
],
516519
[
517-
'$unwind' => '$relatedDummy_lkup',
520+
'$unwind' => [
521+
'path' => '$relatedDummy_lkup',
522+
'preserveNullAndEmptyArrays' => true,
523+
],
518524
],
519525
[
520526
'$sort' => [
@@ -546,6 +552,30 @@ public static function provideApplyTestData(): array
546552
$orderFilterFactory,
547553
EmbeddedDummy::class,
548554
],
555+
'nullable field in relation will be a LEFT JOIN' => [
556+
[
557+
[
558+
'$lookup' => [
559+
'from' => 'RelatedDummy',
560+
'localField' => 'relatedDummy',
561+
'foreignField' => '_id',
562+
'as' => 'relatedDummy_lkup',
563+
],
564+
],
565+
[
566+
'$unwind' => [
567+
'path' => '$relatedDummy_lkup',
568+
'preserveNullAndEmptyArrays' => true,
569+
],
570+
],
571+
[
572+
'$sort' => [
573+
'relatedDummy_lkup.name' => 1,
574+
],
575+
],
576+
],
577+
$orderFilterFactory,
578+
],
549579
]
550580
);
551581
}

tests/Doctrine/Orm/Filter/OrderFilterTest.php

+5
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,11 @@ public static function provideApplyTestData(): array
414414
$orderFilterFactory,
415415
EmbeddedDummy::class,
416416
],
417+
'nullable field in relation will be a LEFT JOIN' => [
418+
sprintf('SELECT o FROM %s o LEFT JOIN o.relatedDummy relatedDummy_a1 ORDER BY relatedDummy_a1.name ASC', Dummy::class),
419+
null,
420+
$orderFilterFactory,
421+
],
417422
]
418423
);
419424
}

0 commit comments

Comments
 (0)