Passed
Pull Request — master (#1999)
by Antoine
02:58
created

Pagination   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 149
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 32
eloc 65
dl 0
loc 149
rs 9.84
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
A getPage() 0 7 2
A getOffset() 0 11 6
B getLimit() 0 35 9
A getParameterFromRequest() 0 7 3
A isEnabled() 0 3 1
A isPartialEnabled() 0 3 1
B getEnabled() 0 22 9
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\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
17
use Symfony\Component\HttpFoundation\Request;
18
use Symfony\Component\HttpFoundation\RequestStack;
19
20
/**
21
 * Pagination configuration.
22
 *
23
 * @author Baptiste Meyer <[email protected]>
24
 */
25
final class Pagination
26
{
27
    private $options;
28
    private $requestStack;
29
    private $resourceMetadataFactory;
30
31
    public function __construct(RequestStack $requestStack, ResourceMetadataFactoryInterface $resourceMetadataFactory, array $options = [])
32
    {
33
        $this->requestStack = $requestStack;
34
        $this->resourceMetadataFactory = $resourceMetadataFactory;
35
        $this->options = array_merge([
36
            'enabled' => true,
37
            'client_enabled' => false,
38
            'client_items_per_page' => false,
39
            'items_per_page' => 30,
40
            'page_default' => 1,
41
            'page_parameter_name' => 'page',
42
            'enabled_parameter_name' => 'pagination',
43
            'items_per_page_parameter_name' => 'itemsPerPage',
44
            'maximum_items_per_page' => null,
45
            'partial' => false,
46
            'client_partial' => false,
47
            'partial_parameter_name' => 'partial',
48
        ], $options);
49
    }
50
51
    /**
52
     * Gets the current page.
53
     */
54
    public function getPage(): int
55
    {
56
        if (($request = $this->requestStack->getCurrentRequest())) {
57
            return (int) $this->getParameterFromRequest($request, $this->options['page_parameter_name'], $this->options['page_default']);
58
        }
59
60
        return $this->options['page_default'];
61
    }
62
63
    /**
64
     * Gets the current offset.
65
     */
66
    public function getOffset(string $resourceClass = null, string $operationName = null): int
67
    {
68
        if (null !== $resourceClass && ($request = $this->requestStack->getCurrentRequest()) && $request->attributes->get('_graphql')) {
69
            $collectionArgs = $request->attributes->get('_graphql_collections_args', []);
70
71
            if (isset($collectionArgs[$resourceClass]['after'])) {
72
                return false === ($after = \base64_decode($collectionArgs[$resourceClass]['after'], true)) ? 0 : (int) $after + 1;
73
            }
74
        }
75
76
        return ($this->getPage() - 1) * $this->getLimit($resourceClass, $operationName);
77
    }
78
79
    /**
80
     * Gets the current limit.
81
     */
82
    public function getLimit(string $resourceClass = null, string $operationName = null): int
83
    {
84
        $limit = $this->options['items_per_page'];
85
        $clientLimit = $this->options['client_items_per_page'];
86
        $request = $this->requestStack->getCurrentRequest();
87
88
        if (null !== $resourceClass) {
89
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
90
            $limit = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_items_per_page', $limit, true);
91
92
            if ($request) {
93
                $clientLimit = $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_client_items_per_page', $clientLimit, true);
94
95
                if ($request->attributes->get('_graphql')) {
96
                    $collectionArgs = $request->attributes->get('_graphql_collections_args', []);
97
                    $limit = $collectionArgs[$resourceClass]['first'] ?? $limit;
98
                }
99
            }
100
        }
101
102
        if ($clientLimit && $request) {
103
            $limit = (int) $this->getParameterFromRequest($request, $this->options['items_per_page_parameter_name'], $limit);
104
            $maxItemsPerPage = $this->options['maximum_items_per_page'];
105
106
            if (null !== $resourceClass) {
107
                $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
108
                $maxItemsPerPage = $resourceMetadata->getCollectionOperationAttribute($operationName, 'maximum_items_per_page', $maxItemsPerPage, true);
109
            }
110
111
            if (null !== $maxItemsPerPage && $limit > $maxItemsPerPage) {
112
                $limit = $maxItemsPerPage;
113
            }
114
        }
115
116
        return $limit;
117
    }
118
119
    /**
120
     * Is the pagination enabled?
121
     */
122
    public function isEnabled(string $resourceClass = null, string $operationName = null): bool
123
    {
124
        return $this->getEnabled($resourceClass, $operationName);
125
    }
126
127
    /**
128
     * Is the partial pagination enabled?
129
     */
130
    public function isPartialEnabled(string $resourceClass = null, string $operationName = null): bool
131
    {
132
        return $this->getEnabled($resourceClass, $operationName, true);
133
    }
134
135
    /**
136
     * Is the classic or partial pagination enabled?
137
     */
138
    private function getEnabled(string $resourceClass = null, string $operationName = null, bool $partial = false): bool
139
    {
140
        $enabled = $this->options[$partial ? 'partial' : 'enabled'];
141
        $clientEnabled = $this->options[$partial ? 'client_partial' : 'client_enabled'];
142
        $request = $this->requestStack->getCurrentRequest();
143
144
        if (null === $request) {
145
            return false;
146
        }
147
148
        if (null !== $resourceClass) {
149
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
150
            $enabled = $resourceMetadata->getCollectionOperationAttribute($operationName, $partial ? 'pagination_partial' : 'pagination_enabled', $enabled, true);
151
152
            $clientEnabled = $resourceMetadata->getCollectionOperationAttribute($operationName, $partial ? 'pagination_client_partial' : 'pagination_client_enabled', $clientEnabled, true);
153
        }
154
155
        if ($clientEnabled) {
156
            return filter_var($this->getParameterFromRequest($request, $this->options[$partial ? 'partial_parameter_name' : 'enabled_parameter_name'], $enabled), FILTER_VALIDATE_BOOLEAN);
157
        }
158
159
        return $enabled;
160
    }
161
162
    /**
163
     * Gets the given pagination parameter name from the given request.
164
     *
165
     * @return mixed
166
     */
167
    private function getParameterFromRequest(Request $request, string $parameterName, $default = null)
168
    {
169
        if (null !== $paginationAttribute = $request->attributes->get('_api_pagination')) {
170
            return array_key_exists($parameterName, $paginationAttribute) ? $paginationAttribute[$parameterName] : $default;
171
        }
172
173
        return $request->query->get($parameterName, $default);
174
    }
175
}
176