Passed
Push — master ( e2a719...9eb666 )
by Alan
03:22
created

PaginationExtension::addCountToContext()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 3
nop 2
dl 0
loc 11
rs 10
c 0
b 0
f 0
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
        $context = $this->addCountToContext(clone $aggregationBuilder, $context);
56
57
        [, $offset, $limit] = $this->pagination->getPagination($resourceClass, $operationName, $context);
58
59
        $manager = $this->managerRegistry->getManagerForClass($resourceClass);
60
        if (!$manager instanceof DocumentManager) {
61
            throw new RuntimeException(sprintf('The manager for "%s" must be an instance of "%s".', $resourceClass, DocumentManager::class));
62
        }
63
64
        $repository = $manager->getRepository($resourceClass);
65
        if (!$repository instanceof DocumentRepository) {
66
            throw new RuntimeException(sprintf('The repository for "%s" must be an instance of "%s".', $resourceClass, DocumentRepository::class));
67
        }
68
69
        $resultsAggregationBuilder = $repository->createAggregationBuilder()->skip($offset);
70
        if ($limit > 0) {
71
            $resultsAggregationBuilder->limit($limit);
72
        } else {
73
            // Results have to be 0 but MongoDB does not support a limit equal to 0.
74
            $resultsAggregationBuilder->match()->field(Paginator::LIMIT_ZERO_MARKER_FIELD)->equals(Paginator::LIMIT_ZERO_MARKER);
75
        }
76
77
        $aggregationBuilder
78
            ->facet()
79
            ->field('results')->pipeline(
80
                $resultsAggregationBuilder
81
            )
82
            ->field('count')->pipeline(
83
                $repository->createAggregationBuilder()
84
                    ->count('count')
85
            );
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function supportsResult(string $resourceClass, string $operationName = null, array $context = []): bool
92
    {
93
        return $this->pagination->isEnabled($resourceClass, $operationName, $context);
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     *
99
     * @throws RuntimeException
100
     */
101
    public function getResult(Builder $aggregationBuilder, string $resourceClass, string $operationName = null, array $context = [])
102
    {
103
        $manager = $this->managerRegistry->getManagerForClass($resourceClass);
104
        if (!$manager instanceof DocumentManager) {
105
            throw new RuntimeException(sprintf('The manager for "%s" must be an instance of "%s".', $resourceClass, DocumentManager::class));
106
        }
107
108
        return new Paginator($aggregationBuilder->execute(), $manager->getUnitOfWork(), $resourceClass, $aggregationBuilder->getPipeline());
109
    }
110
111
    private function addCountToContext(Builder $aggregationBuilder, array $context): array
112
    {
113
        if (!($context['graphql'] ?? false)) {
114
            return $context;
115
        }
116
117
        if (isset($context['filters']['last']) && !isset($context['filters']['before'])) {
118
            $context['count'] = $aggregationBuilder->count('count')->execute()->toArray()[0]['count'];
119
        }
120
121
        return $context;
122
    }
123
}
124