Passed
Pull Request — master (#2144)
by Alan
04:21 queued 18s
created

PaginationExtension::applyToCollection()   C

Complexity

Conditions 12
Paths 62

Size

Total Lines 56
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 36
nc 62
nop 4
dl 0
loc 56
rs 6.9666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
 * This file is part of the API Platform project.
15
 *
16
 * (c) Kévin Dunglas <[email protected]>
17
 *
18
 * For the full copyright and license information, please view the LICENSE
19
 * file that was distributed with this source code.
20
 */
21
22
namespace ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Extension;
23
24
use ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Paginator;
25
use ApiPlatform\Core\Exception\InvalidArgumentException;
26
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
27
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
28
use Doctrine\Common\Persistence\ManagerRegistry;
29
use Doctrine\ODM\MongoDB\Aggregation\Builder;
30
use Symfony\Component\HttpFoundation\Request;
31
use Symfony\Component\HttpFoundation\RequestStack;
32
33
/**
34
 * Applies pagination on the Doctrine aggregation for resource collection when enabled.
35
 *
36
 * @author Kévin Dunglas <[email protected]>
37
 * @author Samuel ROZE <[email protected]>
38
 * @author Alan Poulain <[email protected]>
39
 */
40
final class PaginationExtension implements AggregationResultCollectionExtensionInterface
41
{
42
    private $managerRegistry;
43
    private $requestStack;
44
    private $resourceMetadataFactory;
45
    private $enabled;
46
    private $clientEnabled;
47
    private $clientItemsPerPage;
48
    private $itemsPerPage;
49
    private $pageParameterName;
50
    private $enabledParameterName;
51
    private $itemsPerPageParameterName;
52
    private $maximumItemPerPage;
53
54
    public function __construct(ManagerRegistry $managerRegistry, RequestStack $requestStack, ResourceMetadataFactoryInterface $resourceMetadataFactory, bool $enabled = true, bool $clientEnabled = false, bool $clientItemsPerPage = false, int $itemsPerPage = 30, string $pageParameterName = 'page', string $enabledParameterName = 'pagination', string $itemsPerPageParameterName = 'itemsPerPage', int $maximumItemPerPage = null)
55
    {
56
        $this->managerRegistry = $managerRegistry;
57
        $this->requestStack = $requestStack;
58
        $this->resourceMetadataFactory = $resourceMetadataFactory;
59
        $this->enabled = $enabled;
60
        $this->clientEnabled = $clientEnabled;
61
        $this->clientItemsPerPage = $clientItemsPerPage;
62
        $this->itemsPerPage = $itemsPerPage;
63
        $this->pageParameterName = $pageParameterName;
64
        $this->enabledParameterName = $enabledParameterName;
65
        $this->itemsPerPageParameterName = $itemsPerPageParameterName;
66
        $this->maximumItemPerPage = $maximumItemPerPage;
67
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72
    public function applyToCollection(Builder $aggregationBuilder, string $resourceClass, string $operationName = null, array &$context = [])
73
    {
74
        $request = $this->requestStack->getCurrentRequest();
75
        if (null === $request) {
76
            return;
77
        }
78
79
        $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
80
        if (!$this->isPaginationEnabled($request, $resourceMetadata, $operationName)) {
81
            return;
82
        }
83
84
        $itemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_items_per_page', $this->itemsPerPage, true);
85
        if ($request->attributes->get('_graphql')) {
86
            $collectionArgs = $request->attributes->get('_graphql_collections_args', []);
87
            $itemsPerPage = $collectionArgs[$resourceClass]['first'] ?? $itemsPerPage;
88
        }
89
90
        if ($resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_client_items_per_page', $this->clientItemsPerPage, true)) {
91
            $maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'maximum_items_per_page', $this->maximumItemPerPage, true);
92
93
            $itemsPerPage = (int) $this->getPaginationParameter($request, $this->itemsPerPageParameterName, $itemsPerPage);
94
            $itemsPerPage = (null !== $maxItemsPerPage && $itemsPerPage >= $maxItemsPerPage ? $maxItemsPerPage : $itemsPerPage);
95
        }
96
97
        if (0 >= $itemsPerPage) {
98
            throw new InvalidArgumentException('Item per page parameter should not be less than 1');
99
        }
100
101
        $page = (int) $this->getPaginationParameter($request, $this->pageParameterName, 1);
102
103
        if (1 > $page) {
104
            throw new InvalidArgumentException('Page should not be less than 1');
105
        }
106
107
        $firstResult = ($page - 1) * $itemsPerPage;
108
        if ($request->attributes->get('_graphql')) {
109
            $collectionArgs = $request->attributes->get('_graphql_collections_args', []);
110
            if (isset($collectionArgs[$resourceClass]['after'])) {
111
                $after = \base64_decode($collectionArgs[$resourceClass]['after'], true);
112
                $firstResult = (int) $after;
113
                $firstResult = false === $after ? $firstResult : ++$firstResult;
114
            }
115
        }
116
117
        $repository = $this->managerRegistry->getManagerForClass($resourceClass)->getRepository($resourceClass);
118
        $aggregationBuilder
119
            ->facet()
120
            ->field('results')->pipeline(
121
                $repository->createAggregationBuilder()
122
                    ->skip($firstResult)
123
                    ->limit($itemsPerPage)
124
            )
125
            ->field('count')->pipeline(
126
                $repository->createAggregationBuilder()
127
                    ->count('count')
128
            );
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    public function supportsResult(string $resourceClass, string $operationName = null, array $context = []): bool
135
    {
136
        $request = $this->requestStack->getCurrentRequest();
137
        if (null === $request) {
138
            return false;
139
        }
140
141
        $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
142
143
        return $this->isPaginationEnabled($request, $resourceMetadata, $operationName);
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function getResult(Builder $aggregationBuilder, string $resourceClass, string $operationName = null, array $context = [])
150
    {
151
        return new Paginator($aggregationBuilder->execute(), $this->managerRegistry->getManagerForClass($resourceClass)->getUnitOfWork(), $resourceClass);
0 ignored issues
show
Bug introduced by
The method getUnitOfWork() does not exist on Doctrine\Common\Persistence\ObjectManager. It seems like you code against a sub-type of said class. However, the method does not exist in Doctrine\Common\Persistence\ObjectManagerDecorator. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

151
        return new Paginator($aggregationBuilder->execute(), $this->managerRegistry->getManagerForClass($resourceClass)->/** @scrutinizer ignore-call */ getUnitOfWork(), $resourceClass);
Loading history...
152
    }
153
154
    private function isPaginationEnabled(Request $request, ResourceMetadata $resourceMetadata, string $operationName = null): bool
155
    {
156
        $enabled = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_enabled', $this->enabled, true);
157
        $clientEnabled = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_client_enabled', $this->clientEnabled, true);
158
159
        if ($clientEnabled) {
160
            $enabled = filter_var($request->query->get($this->enabledParameterName, $enabled), FILTER_VALIDATE_BOOLEAN);
161
        }
162
163
        return $enabled;
164
    }
165
166
    private function getPaginationParameter(Request $request, string $parameterName, $default = null)
167
    {
168
        if (null !== $paginationAttribute = $request->attributes->get('_api_pagination')) {
169
            return array_key_exists($parameterName, $paginationAttribute) ? $paginationAttribute[$parameterName] : $default;
170
        }
171
172
        return $request->query->get($parameterName, $default);
173
    }
174
}
175