Passed
Pull Request — master (#1999)
by
unknown
04:10
created

Pagination::getLimit()   B

Complexity

Conditions 10
Paths 40

Size

Total Lines 39
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 22
dl 0
loc 39
rs 7.6666
c 0
b 0
f 0
cc 10
nc 40
nop 2

How to fix   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
namespace ApiPlatform\Core\DataProvider;
15
16
use ApiPlatform\Core\Exception\InvalidArgumentException;
17
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
18
use Symfony\Component\HttpFoundation\Request;
19
use Symfony\Component\HttpFoundation\RequestStack;
20
21
/**
22
 * Pagination configuration.
23
 *
24
 * @author Baptiste Meyer <[email protected]>
25
 */
26
final class Pagination
27
{
28
    private $options;
29
    private $requestStack;
30
    private $resourceMetadataFactory;
31
32
    public function __construct(RequestStack $requestStack, ResourceMetadataFactoryInterface $resourceMetadataFactory, array $options = [])
33
    {
34
        $this->requestStack = $requestStack;
35
        $this->resourceMetadataFactory = $resourceMetadataFactory;
36
        $this->options = array_merge([
37
            'enabled' => true,
38
            'client_enabled' => false,
39
            'client_items_per_page' => false,
40
            'items_per_page' => 30,
41
            'page_default' => 1,
42
            'page_parameter_name' => 'page',
43
            'enabled_parameter_name' => 'pagination',
44
            'items_per_page_parameter_name' => 'itemsPerPage',
45
            'maximum_items_per_page' => null,
46
            'partial' => false,
47
            'client_partial' => false,
48
            'partial_parameter_name' => 'partial',
49
        ], $options);
50
    }
51
52
    /**
53
     * Gets the current page.
54
     *
55
     * @throws InvalidArgumentException
56
     */
57
    public function getPage(): int
58
    {
59
        if (($request = $this->requestStack->getCurrentRequest())) {
60
            $page = (int) $this->getParameterFromRequest($request, $this->options['page_parameter_name'], $this->options['page_default']);
61
        } else {
62
            $page = $this->options['page_default'];
63
        }
64
65
        if (1 > $page) {
66
            throw new InvalidArgumentException('Page should not be less than 1');
67
        }
68
69
        return $page;
70
    }
71
72
    /**
73
     * Gets the current offset.
74
     */
75
    public function getOffset(string $resourceClass = null, string $operationName = null): int
76
    {
77
        if (null !== $resourceClass && ($request = $this->requestStack->getCurrentRequest()) && $request->attributes->get('_graphql')) {
78
            $collectionArgs = $request->attributes->get('_graphql_collections_args', []);
79
80
            if (isset($collectionArgs[$resourceClass]['after'])) {
81
                return false === ($after = base64_decode($collectionArgs[$resourceClass]['after'], true)) ? 0 : (int) $after + 1;
82
            }
83
        }
84
85
        return ($this->getPage() - 1) * $this->getLimit($resourceClass, $operationName);
86
    }
87
88
    /**
89
     * Gets the current limit.
90
     *
91
     * @throws InvalidArgumentException
92
     */
93
    public function getLimit(string $resourceClass = null, string $operationName = null): int
94
    {
95
        $limit = $this->options['items_per_page'];
96
        $clientLimit = $this->options['client_items_per_page'];
97
        $request = $this->requestStack->getCurrentRequest();
98
99
        if (null !== $resourceClass) {
100
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
101
            $limit = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_items_per_page', $limit, true);
102
103
            if ($request) {
104
                $clientLimit = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_client_items_per_page', $clientLimit, true);
105
106
                if ($request->attributes->get('_graphql')) {
107
                    $collectionArgs = $request->attributes->get('_graphql_collections_args', []);
108
                    $limit = $collectionArgs[$resourceClass]['first'] ?? $limit;
109
                }
110
            }
111
        }
112
113
        if ($clientLimit && $request) {
114
            $limit = (int) $this->getParameterFromRequest($request, $this->options['items_per_page_parameter_name'], $limit);
115
            $maxItemsPerPage = $this->options['maximum_items_per_page'];
116
117
            if (null !== $resourceClass) {
118
                $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
119
                $maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'maximum_items_per_page', $maxItemsPerPage, true);
120
            }
121
122
            if (null !== $maxItemsPerPage && $limit > $maxItemsPerPage) {
123
                $limit = $maxItemsPerPage;
124
            }
125
        }
126
127
        if (0 > $limit) {
128
            throw new InvalidArgumentException('Limit should not be less than 0');
129
        }
130
131
        return $limit;
132
    }
133
134
    /**
135
     * Gets info about the pagination.
136
     *
137
     * Returns an array with the following info as values:
138
     *   - the page {@see Pagination::getPage()}
139
     *   - the offset {@see Pagination::getOffset()}
140
     *   - the limit {@see Pagination::getLimit()}
141
     *
142
     * @throws InvalidArgumentException
143
     */
144
    public function getPagination(string $resourceClass = null, string $operationName = null): array
145
    {
146
        $page = $this->getPage();
147
        $limit = $this->getLimit($resourceClass, $operationName);
148
149
        if (0 === $limit && 1 < $page) {
150
            throw new InvalidArgumentException('Page should not be greater than 1 if limit is equal to 0');
151
        }
152
153
        return [$page, $this->getOffset($resourceClass, $operationName), $limit];
154
    }
155
156
    /**
157
     * Is the pagination enabled?
158
     */
159
    public function isEnabled(string $resourceClass = null, string $operationName = null): bool
160
    {
161
        return $this->getEnabled($resourceClass, $operationName);
162
    }
163
164
    /**
165
     * Is the partial pagination enabled?
166
     */
167
    public function isPartialEnabled(string $resourceClass = null, string $operationName = null): bool
168
    {
169
        return $this->getEnabled($resourceClass, $operationName, true);
170
    }
171
172
    /**
173
     * Is the classic or partial pagination enabled?
174
     */
175
    private function getEnabled(string $resourceClass = null, string $operationName = null, bool $partial = false): bool
176
    {
177
        $enabled = $this->options[$partial ? 'partial' : 'enabled'];
178
        $clientEnabled = $this->options[$partial ? 'client_partial' : 'client_enabled'];
179
        $request = $this->requestStack->getCurrentRequest();
180
181
        if (null === $request) {
182
            return false;
183
        }
184
185
        if (null !== $resourceClass) {
186
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
187
            $enabled = $resourceMetadata->getCollectionOperationAttribute($operationName, $partial ? 'pagination_partial' : 'pagination_enabled', $enabled, true);
188
189
            $clientEnabled = $resourceMetadata->getCollectionOperationAttribute($operationName, $partial ? 'pagination_client_partial' : 'pagination_client_enabled', $clientEnabled, true);
190
        }
191
192
        if ($clientEnabled) {
193
            return filter_var($this->getParameterFromRequest($request, $this->options[$partial ? 'partial_parameter_name' : 'enabled_parameter_name'], $enabled), FILTER_VALIDATE_BOOLEAN);
194
        }
195
196
        return $enabled;
197
    }
198
199
    /**
200
     * Gets the given pagination parameter name from the given request.
201
     */
202
    private function getParameterFromRequest(Request $request, string $parameterName, $default = null)
203
    {
204
        if (null !== $paginationAttribute = $request->attributes->get('_api_pagination')) {
205
            return array_key_exists($parameterName, $paginationAttribute) ? $paginationAttribute[$parameterName] : $default;
206
        }
207
208
        return $request->query->get($parameterName, $default);
209
    }
210
}
211