Passed
Push — master ( 8bd912...d93388 )
by Alan
06:58 queued 02:20
created

PaginationExtension::applyToCollection()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 40
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 24
c 1
b 0
f 0
nc 6
nop 4
dl 0
loc 40
rs 8.6026
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
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\Core\Bridge\Doctrine\MongoDbOdm\Extension;
15
16
use ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Paginator;
17
use ApiPlatform\Core\DataProvider\Pagination;
18
use ApiPlatform\Core\Exception\RuntimeException;
19
use Doctrine\Common\Persistence\ManagerRegistry;
20
use Doctrine\ODM\MongoDB\Aggregation\Builder;
21
use Doctrine\ODM\MongoDB\DocumentManager;
22
use Doctrine\ODM\MongoDB\Repository\DocumentRepository;
23
24
/**
25
 * Applies pagination on the Doctrine aggregation for resource collection when enabled.
26
 *
27
 * @experimental
28
 *
29
 * @author Kévin Dunglas <[email protected]>
30
 * @author Samuel ROZE <[email protected]>
31
 * @author Alan Poulain <[email protected]>
32
 */
33
final class PaginationExtension implements AggregationResultCollectionExtensionInterface
34
{
35
    private $managerRegistry;
36
    private $pagination;
37
38
    public function __construct(ManagerRegistry $managerRegistry, Pagination $pagination)
39
    {
40
        $this->managerRegistry = $managerRegistry;
41
        $this->pagination = $pagination;
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     *
47
     * @throws RuntimeException
48
     */
49
    public function applyToCollection(Builder $aggregationBuilder, string $resourceClass, string $operationName = null, array &$context = [])
50
    {
51
        if (!$this->pagination->isEnabled($resourceClass, $operationName, $context)) {
52
            return;
53
        }
54
55
        if (($context['graphql_operation_name'] ?? false) && !$this->pagination->isGraphQlEnabled($resourceClass, $operationName, $context)) {
56
            return;
57
        }
58
59
        $context = $this->addCountToContext(clone $aggregationBuilder, $context);
60
61
        [, $offset, $limit] = $this->pagination->getPagination($resourceClass, $operationName, $context);
62
63
        $manager = $this->managerRegistry->getManagerForClass($resourceClass);
64
        if (!$manager instanceof DocumentManager) {
65
            throw new RuntimeException(sprintf('The manager for "%s" must be an instance of "%s".', $resourceClass, DocumentManager::class));
66
        }
67
68
        $repository = $manager->getRepository($resourceClass);
69
        if (!$repository instanceof DocumentRepository) {
70
            throw new RuntimeException(sprintf('The repository for "%s" must be an instance of "%s".', $resourceClass, DocumentRepository::class));
71
        }
72
73
        $resultsAggregationBuilder = $repository->createAggregationBuilder()->skip($offset);
74
        if ($limit > 0) {
75
            $resultsAggregationBuilder->limit($limit);
76
        } else {
77
            // Results have to be 0 but MongoDB does not support a limit equal to 0.
78
            $resultsAggregationBuilder->match()->field(Paginator::LIMIT_ZERO_MARKER_FIELD)->equals(Paginator::LIMIT_ZERO_MARKER);
79
        }
80
81
        $aggregationBuilder
82
            ->facet()
83
            ->field('results')->pipeline(
84
                $resultsAggregationBuilder
85
            )
86
            ->field('count')->pipeline(
87
                $repository->createAggregationBuilder()
88
                    ->count('count')
89
            );
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95
    public function supportsResult(string $resourceClass, string $operationName = null, array $context = []): bool
96
    {
97
        if ($context['graphql_operation_name'] ?? false) {
98
            return $this->pagination->isGraphQlEnabled($resourceClass, $operationName, $context);
99
        }
100
101
        return $this->pagination->isEnabled($resourceClass, $operationName, $context);
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     *
107
     * @throws RuntimeException
108
     */
109
    public function getResult(Builder $aggregationBuilder, string $resourceClass, string $operationName = null, array $context = [])
110
    {
111
        $manager = $this->managerRegistry->getManagerForClass($resourceClass);
112
        if (!$manager instanceof DocumentManager) {
113
            throw new RuntimeException(sprintf('The manager for "%s" must be an instance of "%s".', $resourceClass, DocumentManager::class));
114
        }
115
116
        return new Paginator($aggregationBuilder->execute(), $manager->getUnitOfWork(), $resourceClass, $aggregationBuilder->getPipeline());
117
    }
118
119
    private function addCountToContext(Builder $aggregationBuilder, array $context): array
120
    {
121
        if (!($context['graphql_operation_name'] ?? false)) {
122
            return $context;
123
        }
124
125
        if (isset($context['filters']['last']) && !isset($context['filters']['before'])) {
126
            $context['count'] = $aggregationBuilder->count('count')->execute()->toArray()[0]['count'];
127
        }
128
129
        return $context;
130
    }
131
}
132