GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 57f6ab...1a78f7 )
by Andrew
9s
created

JsonApiSerializer::buildRelationships()   B

↳ Parent: JsonApiSerializer

Complexity

Conditions 4
Paths 4

Duplication

Lines 0
Ratio 0 %

Size

Total Lines 27
Code Lines 17

Code Coverage

Tests 17
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 27
ccs 17
cts 17
cp 1
rs 8.5806
cc 4
eloc 17
nc 4
nop 4
crap 4
1
<?php
2
3
/*
4
 * This file is part of the League\Fractal package.
5
 *
6
 * (c) Phil Sturgeon <[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
namespace League\Fractal\Serializer;
13
14
use InvalidArgumentException;
15
use League\Fractal\Pagination\PaginatorInterface;
16
use League\Fractal\Resource\ResourceInterface;
17
18
class JsonApiSerializer extends ArraySerializer
19
{
20
    protected $baseUrl;
21
    protected $rootObjects;
22
23
    /**
24
     * JsonApiSerializer constructor.
25
     *
26
     * @param string $baseUrl
27
     */
28 25
    public function __construct($baseUrl = null)
29
    {
30 25
        $this->baseUrl = $baseUrl;
31 25
        $this->rootObjects = [];
32 25
    }
33
34
    /**
35
     * Serialize a collection.
36
     *
37
     * @param string $resourceKey
38
     * @param array $data
39
     *
40
     * @return array
41
     */
42 17
    public function collection($resourceKey, array $data)
43
    {
44 17
        $resources = [];
45
46 17
        foreach ($data as $resource) {
47 16
            $resources[] = $this->item($resourceKey, $resource)['data'];
48 17
        }
49
50 17
        return ['data' => $resources];
51
    }
52
53
    /**
54
     * Serialize an item.
55
     *
56
     * @param string $resourceKey
57
     * @param array $data
58
     *
59
     * @return array
60
     */
61 25
    public function item($resourceKey, array $data)
62
    {
63 25
        $id = $this->getIdFromData($data);
64
65
        $resource = [
66
            'data' => [
67 24
                'type' => $resourceKey,
68 24
                'id' => "$id",
69 24
                'attributes' => $data,
70 24
            ],
71 24
        ];
72
73 24
        unset($resource['data']['attributes']['id']);
74
75 24
        if ($this->shouldIncludeLinks()) {
76 7
            $resource['data']['links'] = [
77 7
                'self' => "{$this->baseUrl}/$resourceKey/$id",
78
            ];
79 7
        }
80
81 24
        return $resource;
82
    }
83
84
    /**
85
     * Serialize the paginator.
86
     *
87
     * @param PaginatorInterface $paginator
88
     *
89
     * @return array
90
     */
91 3
    public function paginator(PaginatorInterface $paginator)
92
    {
93 3
        $currentPage = (int)$paginator->getCurrentPage();
94 3
        $lastPage = (int)$paginator->getLastPage();
95
96
        $pagination = [
97 3
            'total' => (int)$paginator->getTotal(),
98 3
            'count' => (int)$paginator->getCount(),
99 3
            'per_page' => (int)$paginator->getPerPage(),
100 3
            'current_page' => $currentPage,
101 3
            'total_pages' => $lastPage,
102 3
        ];
103
104 3
        $pagination['links'] = [];
105
106 3
        $pagination['links']['self'] = $paginator->getUrl($currentPage);
107 3
        $pagination['links']['first'] = $paginator->getUrl(1);
108
109 3
        if ($currentPage > 1) {
110 2
            $pagination['links']['prev'] = $paginator->getUrl($currentPage - 1);
111 2
        }
112
113 3
        if ($currentPage < $lastPage) {
114 2
            $pagination['links']['next'] = $paginator->getUrl($currentPage + 1);
115 2
        }
116
117 3
        $pagination['links']['last'] = $paginator->getUrl($lastPage);
118
119 3
        return ['pagination' => $pagination];
120
    }
121
122
    /**
123
     * Serialize the meta.
124
     *
125
     * @param array $meta
126
     *
127
     * @return array
128
     */
129 24
    public function meta(array $meta)
130
    {
131 24
        if (empty($meta)) {
132 19
            return [];
133
        }
134
135 5
        $result['meta'] = $meta;
136
137 5
        if (array_key_exists('pagination', $result['meta'])) {
138 3
            $result['links'] = $result['meta']['pagination']['links'];
139 3
            unset($result['meta']['pagination']['links']);
140 3
        }
141
142 5
        return $result;
143
    }
144
145
    /**
146
     * @return array
147
     */
148 2
    public function null()
149
    {
150
        return [
151 2
            'data' => null,
152 2
        ];
153
    }
154
155
    /**
156
     * Serialize the included data.
157
     *
158
     * @param ResourceInterface $resource
159
     * @param array $data
160
     *
161
     * @return array
162
     */
163 24
    public function includedData(ResourceInterface $resource, array $data)
164
    {
165 24
        list($serializedData, $linkedIds) = $this->pullOutNestedIncludedData($data);
166
167 24
        foreach ($data as $value) {
168 24
            foreach ($value as $includeObject) {
169 15
                if ($this->isNull($includeObject) || $this->isEmpty($includeObject)) {
170 4
                    continue;
171
                }
172
173 13
                $includeObjects = $this->createIncludeObjects($includeObject);
174
175 13 View Code Duplication
                foreach ($includeObjects as $object) {
176 13
                    $includeType = $object['type'];
177 13
                    $includeId = $object['id'];
178 13
                    $cacheKey = "$includeType:$includeId";
179 13
                    if (!array_key_exists($cacheKey, $linkedIds)) {
180 13
                        $serializedData[] = $object;
181 13
                        $linkedIds[$cacheKey] = $object;
182 13
                    }
183 13
                }
184 24
            }
185 24
        }
186
187 24
        return empty($serializedData) ? [] : ['included' => $serializedData];
188
    }
189
190
    /**
191
     * Indicates if includes should be side-loaded.
192
     *
193
     * @return bool
194
     */
195 25
    public function sideloadIncludes()
196
    {
197 25
        return true;
198
    }
199
200
    /**
201
     * @param array $data
202
     * @param array $includedData
203
     *
204
     * @return array
205
     */
206 24
    public function injectData($data, $includedData)
207
    {
208 24
        $relationships = $this->parseRelationships($includedData);
209
210 24
        if (!empty($relationships)) {
211 15
            $data = $this->fillRelationships($data, $relationships);
212 15
        }
213
214 24
        return $data;
215
    }
216
217
    /**
218
     * Hook to manipulate the final sideloaded includes.
219
     * The JSON API specification does not allow the root object to be included
220
     * into the sideloaded `included`-array. We have to make sure it is
221
     * filtered out, in case some object links to the root object in a
222
     * relationship.
223
     *
224
     * @param array $includedData
225
     * @param array $data
226
     *
227
     * @return array
228
     */
229 24
    public function filterIncludes($includedData, $data)
230
    {
231 24
        if (!isset($includedData['included'])) {
232 11
            return $includedData;
233
        }
234
235
        // Create the RootObjects
236 13
        $this->createRootObjects($data);
237
238
        // Filter out the root objects
239 13
        $filteredIncludes = array_filter($includedData['included'], [$this, 'filterRootObject']);
240
241
        // Reset array indizes
242 13
        $includedData['included'] = array_merge([], $filteredIncludes);
243
244 13
        return $includedData;
245
    }
246
247
    /**
248
     * Filter function to delete root objects from array.
249
     *
250
     * @param array $object
251
     *
252
     * @return bool
253
     */
254 13
    protected function filterRootObject($object)
255
    {
256 13
        return !$this->isRootObject($object);
257
    }
258
259
    /**
260
     * Set the root objects of the JSON API tree.
261
     *
262
     * @param array $objects
263
     */
264
    protected function setRootObjects(array $objects = [])
265
    {
266 13
        $this->rootObjects = array_map(function ($object) {
267 13
            return "{$object['type']}:{$object['id']}";
268 13
        }, $objects);
269 13
    }
270
271
    /**
272
     * Determines whether an object is a root object of the JSON API tree.
273
     *
274
     * @param array $object
275
     *
276
     * @return bool
277
     */
278 13
    protected function isRootObject($object)
279
    {
280 13
        $objectKey = "{$object['type']}:{$object['id']}";
281 13
        return in_array($objectKey, $this->rootObjects);
282
    }
283
284
    /**
285
     * @param array $data
286
     *
287
     * @return bool
288
     */
289 15
    protected function isCollection($data)
290
    {
291 15
        return array_key_exists('data', $data) &&
292 15
        array_key_exists(0, $data['data']);
293
    }
294
295
    /**
296
     * @param array $data
297
     *
298
     * @return bool
299
     */
300 15
    protected function isNull($data)
301
    {
302 15
        return array_key_exists('data', $data) && $data['data'] === null;
303
    }
304
305
    /**
306
     * @param array $data
307
     *
308
     * @return bool
309
     */
310 14
    protected function isEmpty($data)
311
    {
312 14
        return array_key_exists('data', $data) && $data['data'] === [];
313
    }
314
315
    /**
316
     * @param array $data
317
     * @param array $relationships
318
     *
319
     * @return array
320
     */
321 15
    protected function fillRelationships($data, $relationships)
322
    {
323 15
        if ($this->isCollection($data)) {
324 7
            foreach ($relationships as $key => $relationship) {
325 7
                $data = $this->fillRelationshipAsCollection($data, $relationship, $key);
326 7
            }
327 7
        } else { // Single resource
328 10
            foreach ($relationships as $key => $relationship) {
329 10
                $data = $this->FillRelationshipAsSingleResource($data, $relationship, $key);
330 10
            }
331
        }
332
333 15
        return $data;
334
    }
335
336
    /**
337
     * @param array $includedData
338
     *
339
     * @return array
340
     */
341 24
    protected function parseRelationships($includedData)
342
    {
343 24
        $relationships = [];
344
345 24
        foreach ($includedData as $key => $inclusion) {
346 24
            foreach ($inclusion as $includeKey => $includeObject) {
347 15
                $relationships = $this->buildRelationships($includeKey, $relationships, $includeObject, $key);
348 24
            }
349 24
        }
350
351 24
        return $relationships;
352
    }
353
354
    /**
355
     * @param array $data
356
     *
357
     * @return integer
358
     */
359 25
    protected function getIdFromData(array $data)
360
    {
361 25
        if (!array_key_exists('id', $data)) {
362 1
            throw new InvalidArgumentException(
363
                'JSON API resource objects MUST have a valid id'
364 1
            );
365
        }
366 24
        return $data['id'];
367
    }
368
369
    /**
370
     * Keep all sideloaded inclusion data on the top level.
371
     *
372
     * @param array $data
373
     *
374
     * @return array
375
     */
376 24
    protected function pullOutNestedIncludedData(array $data)
377
    {
378 24
        $includedData = [];
379 24
        $linkedIds = [];
380
381 24
        foreach ($data as $value) {
382 24
            foreach ($value as $includeObject) {
383 15
                if (isset($includeObject['included'])) {
384 3 View Code Duplication
                    foreach ($includeObject['included'] as $object) {
385 3
                        $includeType = $object['type'];
386 3
                        $includeId = $object['id'];
387 3
                        $cacheKey = "$includeType:$includeId";
388
389 3
                        if (!array_key_exists($cacheKey, $linkedIds)) {
390 3
                            $includedData[] = $object;
391 3
                            $linkedIds[$cacheKey] = $object;
392 3
                        }
393 3
                    }
394 3
                }
395 24
            }
396 24
        }
397
398 24
        return [$includedData, $linkedIds];
399
    }
400
401
    /**
402
     * Whether or not the serializer should include `links` for resource objects.
403
     *
404
     * @return bool
405
     */
406 24
    protected function shouldIncludeLinks()
407
    {
408 24
        return $this->baseUrl !== null;
409
    }
410
411
    /**
412
     * Check if the objects are part of a collection or not
413
     *
414
     * @param $includeObject
415
     *
416
     * @return array
417
     */
418 13
    private function createIncludeObjects($includeObject)
419
    {
420 13
        if ($this->isCollection($includeObject)) {
421 7
            $includeObjects = $includeObject['data'];
422
423 7
            return $includeObjects;
424
        } else {
425 9
            $includeObjects = [$includeObject['data']];
426
427 9
            return $includeObjects;
428
        }
429
    }
430
431
    /**
432
     * Sets the RootObjects, either as collection or not.
433
     *
434
     * @param $data
435
     */
436 13
    private function createRootObjects($data)
437
    {
438 13
        if ($this->isCollection($data)) {
439 6
            $this->setRootObjects($data['data']);
440 6
        } else {
441 7
            $this->setRootObjects([$data['data']]);
442
        }
443 13
    }
444
445
446
    /**
447
     * Loops over the relationships of the provided data and formats it
448
     *
449
     * @param $data
450
     * @param $relationship
451
     * @param $nestedDepth
452
     *
453
     * @return array
454
     */
455 7
    private function fillRelationshipAsCollection($data, $relationship, $nestedDepth)
456
    {
457 7
        foreach ($relationship as $index => $relationshipData) {
458 7
            $data['data'][$index]['relationships'][$nestedDepth] = $relationshipData;
459 7
        }
460
461 7
        return $data;
462
    }
463
464
465
    /**
466
     * @param $data
467
     * @param $relationship
468
     * @param $key
469
     *
470
     * @return array
471
     */
472 10
    private function FillRelationshipAsSingleResource($data, $relationship, $key)
473
    {
474 10
        $data['data']['relationships'][$key] = $relationship[0];
475
476 10
        if ($this->shouldIncludeLinks()) {
477 2
            $data['data']['relationships'][$key] = array_merge([
478
                'links' => [
479 2
                    'self' => "{$this->baseUrl}/{$data['data']['type']}/{$data['data']['id']}/relationships/$key",
480 2
                    'related' => "{$this->baseUrl}/{$data['data']['type']}/{$data['data']['id']}/$key",
481 2
                ],
482 2
            ], $data['data']['relationships'][$key]);
483
484 2
            return $data;
485
        }
486 8
        return $data;
487
    }
488
489
    /**
490
     * @param $includeKey
491
     * @param $relationships
492
     * @param $includeObject
493
     * @param $key
494
     *
495
     * @return array
496
     */
497 15
    private function buildRelationships($includeKey, $relationships, $includeObject, $key)
498
    {
499 15
        $relationships = $this->addIncludekeyToRelationsIfNotSet($includeKey, $relationships);
500
501 15
        if ($this->isNull($includeObject)) {
502 2
            $relationship = $this->null();
503 15
        } elseif ($this->isEmpty($includeObject)) {
504
            $relationship = [
505 2
                'data' => [],
506 2
            ];
507 14
        } elseif ($this->isCollection($includeObject)) {
508 7
            $relationship = ['data' => []];
509
510 7
            $relationship = $this->addIncludedDataToRelationship($includeObject, $relationship);
511 7
        } else {
512
            $relationship = [
513
                'data' => [
514 9
                    'type' => $includeObject['data']['type'],
515 9
                    'id' => $includeObject['data']['id'],
516 9
                ],
517 9
            ];
518
        }
519
520 15
        $relationships[$includeKey][$key] = $relationship;
521
522 15
        return $relationships;
523
    }
524
525
    /**
526
     * @param $includeKey
527
     * @param $relationships
528
     *
529
     * @return array
530
     */
531 15
    private function addIncludekeyToRelationsIfNotSet($includeKey, $relationships)
532
    {
533 15
        if (!array_key_exists($includeKey, $relationships)) {
534 15
            $relationships[$includeKey] = [];
535 15
            return $relationships;
536
        }
537
538 7
        return $relationships;
539
    }
540
541
    /**
542
     * @param $includeObject
543
     * @param $relationship
544
     *
545
     * @return array
546
     */
547 7
    private function addIncludedDataToRelationship($includeObject, $relationship)
548
    {
549 7
        foreach ($includeObject['data'] as $object) {
550 7
            $relationship['data'][] = [
551 7
                'type' => $object['type'],
552 7
                'id' => $object['id'],
553
            ];
554 7
        }
555
556 7
        return $relationship;
557
    }
558
}
559