ListResource::getErrorResponse()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 17
rs 9.4285
cc 2
eloc 10
nc 2
nop 1
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