• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 17770652476

16 Sep 2025 03:19PM UTC coverage: 21.904% (+0.002%) from 21.902%
17770652476

Pull #7387

github

web-flow
Merge 0bb232b94 into 510fa5523
Pull Request #7387: feat(doctrine): Add MongoDB Atlas Search filter

0 of 55 new or added lines in 3 files covered. (0.0%)

2 existing lines in 2 files now uncovered.

11878 of 54227 relevant lines covered (21.9%)

25.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Doctrine/Odm/Extension/OrderExtension.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Doctrine\Odm\Extension;
15

16
use ApiPlatform\Doctrine\Common\PropertyHelperTrait;
17
use ApiPlatform\Doctrine\Odm\PropertyHelperTrait as MongoDbOdmPropertyHelperTrait;
18
use ApiPlatform\Metadata\Operation;
19
use Doctrine\ODM\MongoDB\Aggregation\Builder;
20
use Doctrine\ODM\MongoDB\Aggregation\Stage\Search;
21
use Doctrine\ODM\MongoDB\Aggregation\Stage\Sort;
22
use Doctrine\Persistence\ManagerRegistry;
23

24
/**
25
 * Applies selected ordering while querying resource collection.
26
 *
27
 * @author Kévin Dunglas <dunglas@gmail.com>
28
 * @author Samuel ROZE <samuel.roze@gmail.com>
29
 * @author Vincent Chalamon <vincentchalamon@gmail.com>
30
 * @author Alan Poulain <contact@alanpoulain.eu>
31
 */
32
final class OrderExtension implements AggregationCollectionExtensionInterface
33
{
34
    use MongoDbOdmPropertyHelperTrait;
35
    use PropertyHelperTrait;
36

37
    public function __construct(private readonly ?string $order = null, private readonly ?ManagerRegistry $managerRegistry = null)
38
    {
39
    }
×
40

41
    /**
42
     * {@inheritdoc}
43
     */
44
    public function applyToCollection(Builder $aggregationBuilder, string $resourceClass, ?Operation $operation = null, array &$context = []): void
45
    {
46
        // Do not apply order if already defined on $aggregationBuilder
47
        if ($this->hasSortStage($aggregationBuilder)) {
×
48
            return;
×
49
        }
50

51
        $classMetaData = $this->getClassMetadata($resourceClass);
×
52
        $identifiers = $classMetaData->getIdentifier();
×
53
        if (isset($context['operation'])) {
×
54
            $defaultOrder = $context['operation']->getOrder() ?? [];
×
55
        } else {
56
            $defaultOrder = $operation?->getOrder();
×
57
        }
58

59
        if ($defaultOrder) {
×
60
            foreach ($defaultOrder as $field => $order) {
×
61
                if (\is_int($field)) {
×
62
                    // Default direction
63
                    $field = $order;
×
64
                    $order = 'ASC';
×
65
                }
66

67
                if ($this->isPropertyNested($field, $resourceClass)) {
×
68
                    [$field] = $this->addLookupsForNestedProperty($field, $aggregationBuilder, $resourceClass, true);
×
69
                }
70

NEW
71
                $context['mongodb_odm_sort_fields'] = ($context['mongodb_odm_sort_fields'] ?? []) + [$field => $order];
×
NEW
72
                if ($this->isSearchPipeline($aggregationBuilder)) {
×
NEW
73
                    $aggregationBuilder->getStage(0)
×
NEW
74
                        ->sort($context['mongodb_odm_sort_fields']);
×
75
                } else {
NEW
76
                    $aggregationBuilder->sort($context['mongodb_odm_sort_fields']);
×
77
                }
78
            }
79

80
            return;
×
81
        }
82

NEW
83
        if (null !== $this->order && !$this->isSearchPipeline($aggregationBuilder)) {
×
84
            foreach ($identifiers as $identifier) {
×
85
                $aggregationBuilder->sort(
×
86
                    $context['mongodb_odm_sort_fields'] = ($context['mongodb_odm_sort_fields'] ?? []) + [$identifier => $this->order]
×
87
                );
×
88
            }
89
        }
90
    }
91

92
    protected function getManagerRegistry(): ManagerRegistry
93
    {
94
        return $this->managerRegistry;
×
95
    }
96

97
    private function hasSortStage(Builder $aggregationBuilder): bool
98
    {
99
        try {
NEW
100
            for ($index = 0; true; $index++) {
×
UNCOV
101
                if ($aggregationBuilder->getStage($index) instanceof Sort) {
×
102
                    // If at least one stage is sort, then it has sorting
103
                    return true;
×
104
                }
105
            }
NEW
106
        } catch (\OutOfRangeException) {
×
107
            // There is no more stages on the aggregation builder
NEW
108
            return false;
×
109
        }
110
    }
111

112
    private function isSearchPipeline(Builder $aggregationBuilder): bool
113
    {
114
        try {
NEW
115
            return $aggregationBuilder->getStage(0) instanceof Search;
×
NEW
116
        } catch (\OutOfRangeException) {
×
117
            // Empty pipeline
NEW
118
            return false;
×
119
        }
120
    }
121
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc