• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 14635100171

24 Apr 2025 06:39AM UTC coverage: 8.271% (+0.02%) from 8.252%
14635100171

Pull #6904

github

web-flow
Merge c9cefd82e into a3e5e53ea
Pull Request #6904: feat(graphql): added support for graphql subscriptions to work for actions

0 of 73 new or added lines in 3 files covered. (0.0%)

1999 existing lines in 144 files now uncovered.

13129 of 158728 relevant lines covered (8.27%)

13.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.05
/src/State/Pagination/Pagination.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
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\State\Pagination;
15

16
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
17
use ApiPlatform\Metadata\Operation;
18

19
/**
20
 * Pagination configuration.
21
 *
22
 * @author Baptiste Meyer <baptiste.meyer@gmail.com>
23
 */
24
final class Pagination
25
{
26
    private readonly array $options;
27
    private readonly array $graphQlOptions;
28

29
    public function __construct(array $options = [], array $graphQlOptions = [])
30
    {
31
        $this->options = array_merge([
787✔
32
            'enabled' => true,
787✔
33
            'client_enabled' => false,
787✔
34
            'client_items_per_page' => false,
787✔
35
            'items_per_page' => 30,
787✔
36
            'page_default' => 1,
787✔
37
            'page_parameter_name' => 'page',
787✔
38
            'enabled_parameter_name' => 'pagination',
787✔
39
            'items_per_page_parameter_name' => 'itemsPerPage',
787✔
40
            'maximum_items_per_page' => null,
787✔
41
            'partial' => false,
787✔
42
            'client_partial' => false,
787✔
43
            'partial_parameter_name' => 'partial',
787✔
44
        ], $options);
787✔
45
        $this->graphQlOptions = array_merge([
787✔
46
            'enabled' => true,
787✔
47
        ], $graphQlOptions);
787✔
48
    }
49

50
    /**
51
     * Gets the current page.
52
     *
53
     * @throws InvalidArgumentException
54
     */
55
    public function getPage(array $context = []): int
56
    {
57
        $page = (int) $this->getParameterFromContext(
473✔
58
            $context,
473✔
59
            $this->options['page_parameter_name'],
473✔
60
            $this->options['page_default']
473✔
61
        );
473✔
62

63
        if (1 > $page) {
473✔
UNCOV
64
            throw new InvalidArgumentException('Page should not be less than 1');
1✔
65
        }
66

67
        return $page;
472✔
68
    }
69

70
    /**
71
     * Gets the current offset.
72
     */
73
    public function getOffset(?Operation $operation = null, array $context = []): int
74
    {
75
        $graphql = (bool) ($context['graphql_operation_name'] ?? false);
472✔
76

77
        $limit = $this->getLimit($operation, $context);
472✔
78

79
        if ($graphql && null !== ($after = $this->getParameterFromContext($context, 'after'))) {
472✔
UNCOV
80
            return false === ($after = base64_decode($after, true)) ? 0 : (int) $after + 1;
1✔
81
        }
82

83
        if ($graphql && null !== ($before = $this->getParameterFromContext($context, 'before'))) {
472✔
UNCOV
84
            return ($offset = (false === ($before = base64_decode($before, true)) ? 0 : (int) $before - $limit)) < 0 ? 0 : $offset;
1✔
85
        }
86

87
        if ($graphql && null !== ($last = $this->getParameterFromContext($context, 'last'))) {
472✔
UNCOV
88
            return ($offset = ($context['count'] ?? 0) - $last) < 0 ? 0 : $offset;
1✔
89
        }
90

91
        $offset = ($this->getPage($context) - 1) * $limit;
471✔
92

93
        if (!\is_int($offset)) {
471✔
UNCOV
94
            throw new InvalidArgumentException('Page parameter is too large.');
1✔
95
        }
96

97
        return $offset;
470✔
98
    }
99

100
    /**
101
     * Gets the current limit.
102
     *
103
     * @throws InvalidArgumentException
104
     */
105
    public function getLimit(?Operation $operation = null, array $context = []): int
106
    {
107
        $graphql = (bool) ($context['graphql_operation_name'] ?? false);
472✔
108

109
        $limit = $operation?->getPaginationItemsPerPage() ?? $this->options['items_per_page'];
472✔
110
        $clientLimit = $operation?->getPaginationClientItemsPerPage() ?? $this->options['client_items_per_page'];
472✔
111

112
        if ($graphql && null !== ($first = $this->getParameterFromContext($context, 'first'))) {
472✔
UNCOV
113
            $limit = $first;
5✔
114
        }
115

116
        if ($graphql && null !== ($last = $this->getParameterFromContext($context, 'last'))) {
472✔
UNCOV
117
            $limit = $last;
1✔
118
        }
119

120
        if ($graphql && null !== ($before = $this->getParameterFromContext($context, 'before'))
472✔
121
            && (false === ($before = base64_decode($before, true)) ? 0 : (int) $before - $limit) < 0) {
472✔
UNCOV
122
            $limit = (int) $before;
1✔
123
        }
124

125
        if ($clientLimit) {
472✔
126
            $limit = (int) $this->getParameterFromContext($context, $this->options['items_per_page_parameter_name'], $limit);
472✔
127
            $maxItemsPerPage = $operation?->getPaginationMaximumItemsPerPage() ?? $this->options['maximum_items_per_page'];
472✔
128

129
            if (null !== $maxItemsPerPage && $limit > $maxItemsPerPage) {
472✔
130
                $limit = $maxItemsPerPage;
×
131
            }
132
        }
133

134
        if (0 > $limit) {
472✔
135
            throw new InvalidArgumentException('Limit should not be less than 0');
×
136
        }
137

138
        return $limit;
472✔
139
    }
140

141
    /**
142
     * Gets info about the pagination.
143
     *
144
     * Returns an array with the following info as values:
145
     *   - the page {@see Pagination::getPage}
146
     *   - the offset {@see Pagination::getOffset}
147
     *   - the limit {@see Pagination::getLimit}
148
     *
149
     * @throws InvalidArgumentException
150
     */
151
    public function getPagination(?Operation $operation = null, array $context = []): array
152
    {
153
        $page = $this->getPage($context);
473✔
154
        $limit = $this->getLimit($operation, $context);
472✔
155

156
        if (0 === $limit && 1 < $page) {
472✔
UNCOV
157
            throw new InvalidArgumentException('Page should not be greater than 1 if limit is equal to 0');
1✔
158
        }
159

160
        return [$page, $this->getOffset($operation, $context), $limit];
472✔
161
    }
162

163
    /**
164
     * Is the pagination enabled?
165
     */
166
    public function isEnabled(?Operation $operation = null, array $context = []): bool
167
    {
168
        return $this->getEnabled($context, $operation);
472✔
169
    }
170

171
    /**
172
     * Is the pagination enabled for GraphQL?
173
     */
174
    public function isGraphQlEnabled(?Operation $operation = null, array $context = []): bool
175
    {
176
        return $this->getGraphQlEnabled($operation);
147✔
177
    }
178

179
    /**
180
     * Is the partial pagination enabled?
181
     */
182
    public function isPartialEnabled(?Operation $operation = null, array $context = []): bool
183
    {
184
        return $this->getEnabled($context, $operation, true);
202✔
185
    }
186

187
    public function getOptions(): array
188
    {
UNCOV
189
        return $this->options;
133✔
190
    }
191

192
    public function getGraphQlPaginationType(Operation $operation): string
193
    {
194
        return $operation->getPaginationType() ?? 'cursor';
145✔
195
    }
196

197
    /**
198
     * Is the classic or partial pagination enabled?
199
     */
200
    private function getEnabled(array $context, ?Operation $operation = null, bool $partial = false): bool
201
    {
202
        $enabled = $this->options[$partial ? 'partial' : 'enabled'];
476✔
203
        $clientEnabled = $this->options[$partial ? 'client_partial' : 'client_enabled'];
476✔
204

205
        $enabled = ($partial ? $operation?->getPaginationPartial() : $operation?->getPaginationEnabled()) ?? $enabled;
476✔
206
        $clientEnabled = ($partial ? $operation?->getPaginationClientPartial() : $operation?->getPaginationClientEnabled()) ?? $clientEnabled;
476✔
207

208
        if ($clientEnabled) {
476✔
209
            return filter_var($this->getParameterFromContext($context, $this->options[$partial ? 'partial_parameter_name' : 'enabled_parameter_name'], $enabled), \FILTER_VALIDATE_BOOLEAN);
476✔
210
        }
211

212
        return (bool) $enabled;
×
213
    }
214

215
    private function getGraphQlEnabled(?Operation $operation): bool
216
    {
217
        $enabled = $this->graphQlOptions['enabled'];
147✔
218

219
        return $operation?->getPaginationEnabled() ?? $enabled;
147✔
220
    }
221

222
    /**
223
     * Gets the given pagination parameter name from the given context.
224
     */
225
    private function getParameterFromContext(array $context, string $parameterName, mixed $default = null)
226
    {
227
        $filters = $context['filters'] ?? [];
476✔
228

229
        return \array_key_exists($parameterName, $filters) ? $filters[$parameterName] : $default;
476✔
230
    }
231
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc