ListResource::pagePaginationLinks()   B
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 38
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 38
rs 8.439
cc 5
eloc 28
nc 4
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * Author: Nil Portugués Calderó <[email protected]>
4
 * Date: 11/28/15
5
 * Time: 8:03 PM.
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace NilPortugues\Api\JsonApi\Server\Actions;
11
12
use Exception;
13
use NilPortugues\Api\JsonApi\Http\PaginatedResource;
14
use NilPortugues\Api\JsonApi\Http\Request\Parameters\Fields;
15
use NilPortugues\Api\JsonApi\Http\Request\Parameters\Included;
16
use NilPortugues\Api\JsonApi\Http\Request\Parameters\Page;
17
use NilPortugues\Api\JsonApi\Http\Request\Parameters\Sorting;
18
use NilPortugues\Api\JsonApi\JsonApiSerializer;
19
use NilPortugues\Api\JsonApi\Server\Actions\Traits\RequestTrait;
20
use NilPortugues\Api\JsonApi\Server\Actions\Traits\ResponseTrait;
21
use NilPortugues\Api\JsonApi\Server\Errors\Error;
22
use NilPortugues\Api\JsonApi\Server\Errors\ErrorBag;
23
use NilPortugues\Api\JsonApi\Server\Errors\OufOfBoundsError;
24
use NilPortugues\Api\JsonApi\Server\Query\QueryException;
25
use NilPortugues\Api\JsonApi\Server\Query\QueryObject;
26
27
/**
28
 * Class ListResource.
29
 */
30
class ListResource
31
{
32
    use RequestTrait;
33
    use ResponseTrait;
34
35
    /**
36
     * @var \NilPortugues\Api\JsonApi\Server\Errors\ErrorBag
37
     */
38
    protected $errorBag;
39
40
    /**
41
     * @var Page
42
     */
43
    protected $page;
44
    /**
45
     * @var Fields
46
     */
47
    protected $fields;
48
    /**
49
     * @var Sorting
50
     */
51
    protected $sorting;
52
    /**
53
     * @var Included
54
     */
55
    protected $included;
56
    /**
57
     * @var array
58
     */
59
    protected $filters;
60
61
    /**
62
     * @var JsonApiSerializer
63
     */
64
    protected $serializer;
65
66
    /**
67
     * @param JsonApiSerializer $serializer
68
     * @param Page              $page
69
     * @param Fields            $fields
70
     * @param Sorting           $sorting
71
     * @param Included          $included
72
     * @param array             $filters
73
     */
74
    public function __construct(
75
        JsonApiSerializer $serializer,
76
        Page $page,
77
        Fields $fields,
78
        Sorting $sorting,
79
        Included $included,
80
        $filters
81
    ) {
82
        $this->serializer = $serializer;
83
        $this->errorBag = new ErrorBag();
84
        $this->page = $page;
85
        $this->fields = $fields;
86
        $this->sorting = $sorting;
87
        $this->included = $included;
88
        $this->filters = $filters;
89
    }
90
91
    /**
92
     * @param callable $totalAmountCallable
93
     * @param callable $resultsCallable
94
     * @param string   $route
95
     * @param string   $className
96
     *
97
     * @return \Symfony\Component\HttpFoundation\Response
98
     */
99
    public function get(callable $totalAmountCallable, callable $resultsCallable, $route, $className)
100
    {
101
        try {
102
            QueryObject::assert(
103
                $this->serializer,
104
                $this->fields,
105
                $this->included,
106
                $this->sorting,
107
                $this->errorBag,
108
                $className
109
            );
110
            $totalAmount = $totalAmountCallable();
111
112
            if ($totalAmount > 0 && $this->page->size() > 0 && $this->page->number() > ceil($totalAmount / $this->page->size())) {
113
                return $this->resourceNotFound(
114
                    new ErrorBag([new OufOfBoundsError($this->page->number(), $this->page->size())])
115
                );
116
            }
117
118
            $links = $this->pagePaginationLinks(
119
                $route,
120
                $this->page->number(),
121
                $this->page->size(),
122
                $totalAmount,
123
                $this->fields,
124
                $this->sorting,
125
                $this->included,
126
                $this->filters
127
            );
128
129
            $results = $resultsCallable();
130
131
            $paginatedResource = new PaginatedResource(
132
                $this->serializer->serialize($results, $this->fields, $this->included),
133
                $this->page->number(),
134
                $this->page->size(),
135
                $totalAmount,
136
                $links
137
            );
138
139
            $response = $this->response($paginatedResource);
140
        } catch (Exception $e) {
141
            $response = $this->getErrorResponse($e);
142
        }
143
144
        return $response;
145
    }
146
147
    /**
148
     * @param string   $route
149
     * @param int      $pageNumber
150
     * @param int      $pageSize
151
     * @param int      $totalPages
152
     * @param Fields   $fields
153
     * @param Sorting  $sorting
154
     * @param Included $included
155
     * @param array    $filters
156
     *
157
     * @return array
158
     */
159
    protected function pagePaginationLinks(
160
        $route,
161
        $pageNumber,
162
        $pageSize,
163
        $totalPages,
164
        Fields $fields,
165
        Sorting $sorting,
166
        Included $included,
167
        $filters
168
    ) {
169
        $next = $pageNumber + 1;
170
        $previous = $pageNumber - 1;
171
        $last = ($pageSize == 0) ? 0 : ceil($totalPages / $pageSize);
172
173
        $links = array_filter(
174
            [
175
                'self' => $pageNumber,
176
                'first' => 1,
177
                'next' => ($next <= $last) ? $next : null,
178
                'previous' => ($previous >= 1) ? $previous : null,
179
                'last' => $last,
180
            ]
181
        );
182
183
        foreach ($links as &$numberedLink) {
184
            $numberedLink = $this->pagePaginatedRoute(
185
                $route,
186
                $numberedLink,
187
                $pageSize,
188
                $fields,
189
                $sorting,
190
                $included,
191
                $filters
192
            );
193
        }
194
195
        return $links;
196
    }
197
198
    /**
199
     * Build the URL.
200
     *
201
     * @param string   $route
202
     * @param int      $pageNumber
203
     * @param int      $pageSize
204
     * @param Fields   $fields
205
     * @param Sorting  $sorting
206
     * @param Included $included
207
     * @param array    $filters
208
     *
209
     * @return string
210
     */
211
    protected function pagePaginatedRoute(
212
        $route,
213
        $pageNumber,
214
        $pageSize,
215
        Fields $fields,
216
        Sorting $sorting,
217
        Included $included,
218
        $filters
219
    ) {
220
        $fieldKeys = [];
221
        if (false === $fields->isEmpty()) {
222
            $fieldKeys = $fields->get();
223
            foreach ($fieldKeys as &$v) {
224
                $v = implode(',', $v);
225
            }
226
        }
227
228
        $queryParams = urldecode(
229
            http_build_query(
230
                array_filter([
231
                        'page' => array_filter(
232
                            [
233
                                'number' => $pageNumber,
234
                                'size' => $pageSize,
235
                            ]
236
                        ),
237
                        'fields' => $fieldKeys,
238
                        'filter' => $filters,
239
                        'sort' => $sorting->get(),
240
                        'include' => $included->get(),
241
                    ])
242
            )
243
        );
244
245
        $expression = ($route[strlen($route) - 1] === '?' || $route[strlen($route) - 1] === '&') ? '%s%s' : '%s?%s';
246
247
        return sprintf($expression, $route, $queryParams);
248
    }
249
250
    /**
251
     * @param Exception $e
252
     *
253
     * @return \Symfony\Component\HttpFoundation\Response
254
     */
255
    protected function getErrorResponse(Exception $e)
256
    {
257
        switch (get_class($e)) {
258
            case QueryException::class:
259
                $response = $this->errorResponse($this->errorBag);
260
                break;
261
262
            default:
263
                $response = $this->errorResponse(
264
                    new ErrorBag([new Error('Bad Request', 'Request could not be served.')])
265
                );
266
267
                return $response;
268
        }
269
270
        return $response;
271
    }
272
}
273