Completed
Pull Request — master (#45)
by Jasper
11:48
created

Item::toJsonApiArray()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 21
ccs 10
cts 10
cp 1
rs 9.9
c 0
b 0
f 0
cc 4
nc 8
nop 0
crap 4
1
<?php
2
3
namespace Swis\JsonApi\Client;
4
5
use Jenssegers\Model\Model;
6
use Swis\JsonApi\Client\Interfaces\DataInterface;
7
use Swis\JsonApi\Client\Interfaces\ItemInterface;
8
use Swis\JsonApi\Client\Interfaces\ManyRelationInterface;
9
use Swis\JsonApi\Client\Interfaces\OneRelationInterface;
10
use Swis\JsonApi\Client\Relations\HasManyRelation;
11
use Swis\JsonApi\Client\Relations\HasOneRelation;
12
use Swis\JsonApi\Client\Relations\MorphToManyRelation;
13
use Swis\JsonApi\Client\Relations\MorphToRelation;
14
15
class Item extends Model implements ItemInterface
16
{
17
    /**
18
     * @var string
19
     */
20
    protected $type;
21
22
    /**
23
     * @var
24
     */
25
    protected $id;
26
27
    /**
28
     * Contains the initial values.
29
     *
30
     * @var array
31
     */
32
    protected $initial = [];
33
34
    /**
35
     * @var \Swis\JsonApi\Client\Interfaces\OneRelationInterface[]|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface[]
36
     */
37
    protected $relationships = [];
38
39
    /**
40
     * Available relations need to be explicitly set.
41
     *
42
     * @var array
43
     */
44
    protected $availableRelations = [];
45
46
    /**
47
     * @var array
48
     */
49
    protected $guarded = ['id'];
50
51
    /**
52 95
     * @return array
53
     */
54
    public function toJsonApiArray(): array
55 95
    {
56
        $data = [
57
            'type' => $this->getType(),
58 95
        ];
59 55
60
        if ($this->hasId()) {
61
            $data['id'] = $this->getId();
62 95
        }
63 95
64 10
        $attributes = $this->toArray();
65
        if (!empty($attributes)) {
66
            $data['attributes'] = $attributes;
67 95
        }
68 95
69 80
        $relationships = $this->getRelationships();
70
        if (!empty($relationships)) {
71
            $data['relationships'] = $relationships;
72 95
        }
73
74
        return $data;
75
    }
76
77
    /**
78 180
     * @return string
79
     */
80 180
    public function getType(): string
81
    {
82
        return $this->type;
83
    }
84
85
    /**
86
     * @param string $type
87
     *
88 75
     * @return static
89
     */
90 75
    public function setType(string $type)
91
    {
92 75
        $this->type = $type;
93
94
        return $this;
95
    }
96
97
    /**
98 15
     * @return bool
99
     */
100 15
    public function isNew(): bool
101
    {
102
        return !$this->hasId();
103
    }
104
105
    /**
106 115
     * @return bool
107
     */
108 115
    public function hasId(): bool
109
    {
110
        return isset($this->id);
111
    }
112
113
    /**
114 230
     * @return string
115
     */
116 230
    public function getId()
117
    {
118
        return $this->id;
119
    }
120
121
    /**
122
     * @param string $id
123
     *
124 225
     * @return static
125
     */
126 225
    public function setId($id)
127
    {
128 225
        $this->id = $id;
129
130
        return $this;
131
    }
132
133
    /**
134 100
     * @return array
135
     */
136 100
    public function getRelationships(): array
137
    {
138
        $relationships = [];
139 100
140 85
        foreach ($this->relationships as $name => $relationship) {
141 25
            if ($relationship instanceof OneRelationInterface) {
142
                $relationships[$name] = ['data' => null];
143 25
144 20
                if ($relationship->getIncluded() !== null) {
145
                    $relationships[$name] = [
146 20
                        'data' => [
147 25
                            'type' => $relationship->getIncluded()->getType(),
148
                            'id'   => $relationship->getIncluded()->getId(),
149
                        ],
150
                    ];
151 60
                }
152 20
            } elseif ($relationship instanceof ManyRelationInterface) {
153
                $relationships[$name]['data'] = [];
154 20
155 15
                foreach ($relationship->getIncluded() as $item) {
156
                    $relationships[$name]['data'][] =
157 15
                        [
158 17
                            'type' => $item->getType(),
159
                            'id'   => $item->getId(),
160
                        ];
161 40
                }
162 20
            }
163
        }
164 20
165 15
        return $relationships;
166
    }
167 15
168 20
    /**
169
     * @TODO: MEGA TODO. Set up a serializer for the Item so that we can remove this, getRelationships etc
170
     *
171
     * @return \Swis\JsonApi\Client\Collection
172 20
     */
173 20
    public function getIncluded(): Collection
174
    {
175 20
        $included = new Collection();
176 15
177
        foreach ($this->relationships as $name => $relationship) {
178 15
            if ($relationship->shouldOmitIncluded() || !$relationship->hasIncluded()) {
179 43
                continue;
180
            }
181
182
            if ($relationship instanceof OneRelationInterface) {
183
                /** @var \Swis\JsonApi\Client\Interfaces\ItemInterface $item */
184
                $item = $relationship->getIncluded();
185 100
                if ($item->canBeIncluded()) {
186
                    $included->push($item->toJsonApiArray());
187
                }
188
                $included = $included->merge($item->getIncluded());
189
            } elseif ($relationship instanceof ManyRelationInterface) {
190
                $relationship->getIncluded()->each(
191
                    function (ItemInterface $item) use (&$included) {
192
                        if ($item->canBeIncluded()) {
193
                            $included->push($item->toJsonApiArray());
194
                        }
195 35
                        $included = $included->merge($item->getIncluded());
196
                    }
197 35
                );
198
            }
199 35
        }
200
201
        return $included
202
            ->unique(
203
                function (array $item) {
204
                    return $item['type'].':'.$item['id'];
205
                }
206
            )
207
            ->values();
208
    }
209
210
    /**
211
     * @return bool
212
     */
213
    public function canBeIncluded(): bool
214
    {
215
        if (empty($this->getType())) {
216
            return false;
217
        }
218
219
        if (null === $this->getId()) {
220
            return false;
221
        }
222
223
        if (empty($this->relationships) && empty($this->toArray())) {
224
            return false;
225 35
        }
226 21
227
        return true;
228 35
    }
229
230 35
    /**
231
     * @param string $key
232
     *
233
     * @return \Swis\JsonApi\Client\Interfaces\DataInterface|mixed
234
     */
235
    public function getAttribute($key)
236
    {
237
        if ($this->hasAttribute($key) || $this->hasGetMutator($key)) {
238
            return parent::getAttribute($key);
239
        }
240
241
        return $this->getRelationValue($key);
242
    }
243
244
    /**
245
     * @param string $key
246
     *
247
     * @return bool
248
     */
249
    public function hasAttribute($key): bool
250
    {
251
        return array_key_exists($key, $this->attributes);
252
    }
253
254
    /**
255
     * Get the relationship data.
256
     *
257
     * @param string $key
258 55
     *
259
     * @return \Swis\JsonApi\Client\Interfaces\DataInterface|null
260 55
     */
261 35
    public function getRelationValue($key)
262
    {
263
        // If the "attribute" exists as a method on the model, we will just assume
264 20
        // it is a relationship and will load and return the included items in the relationship
265
        $method = camel_case($key);
266
        if (method_exists($this, $method)) {
267
            return $this->$method()->getIncluded();
268
        }
269
270
        // If the "attribute" exists as a relationship on the model, we will return
271
        // the included items in the relationship
272 55
        if ($this->hasRelationship($key)) {
273
            return $this->getRelationship($key)->getIncluded();
274 55
        }
275
276
        return null;
277
    }
278
279
    /**
280
     * Determine if an attribute exists on the model.
281
     *
282
     * @param string $key
283
     *
284 20
     * @return bool
285
     */
286
    public function __isset($key)
287
    {
288 20
        return parent::__isset($key) || $this->hasRelationship($key) || $this->hasRelationship(snake_case($key));
289 20
    }
290 10
291
    /**
292
     * @param string $name
293
     *
294
     * @return \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface
295 10
     */
296 10
    public function getRelationship(string $name)
297
    {
298
        return $this->relationships[$name];
299
    }
300
301
    /**
302
     * @param string $name
303
     *
304
     * @return bool
305
     */
306
    public function hasRelationship(string $name): bool
307
    {
308
        return array_key_exists($name, $this->relationships);
309
    }
310
311
    /**
312
     * @param $name
313
     *
314
     * @return static
315
     */
316
    public function removeRelationship(string $name)
317
    {
318
        unset($this->relationships[$name]);
319
320 80
        return $this;
321
    }
322 80
323
    /**
324
     * Create a singular relation to another item.
325
     *
326
     * @param string      $class
327
     * @param string|null $relationName
328
     *
329
     * @return \Swis\JsonApi\Client\Relations\HasOneRelation
330 25
     */
331
    public function hasOne(string $class, string $relationName = null): HasOneRelation
332 25
    {
333
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
334
335
        if (!array_key_exists($relationName, $this->relationships)) {
336
            $this->relationships[$relationName] = new HasOneRelation((new $class())->getType());
337
        }
338
339
        return $this->relationships[$relationName];
340
    }
341
342
    /**
343
     * Create a plural relation to another item.
344
     *
345
     * @param string      $class
346
     * @param string|null $relationName
347
     *
348
     * @return \Swis\JsonApi\Client\Relations\HasManyRelation
349
     */
350
    public function hasMany(string $class, string $relationName = null): HasManyRelation
351
    {
352
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
353
354
        if (!array_key_exists($relationName, $this->relationships)) {
355 45
            $this->relationships[$relationName] = new HasManyRelation((new $class())->getType());
356
        }
357 45
358 45
        return $this->relationships[$relationName];
359
    }
360 45
361 45
    /**
362
     * Create a singular relation to another item.
363
     *
364 45
     * @param string|null $relationName
365
     *
366
     * @return \Swis\JsonApi\Client\Relations\MorphToRelation
367
     */
368
    public function morphTo(string $relationName = null): MorphToRelation
369
    {
370
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
371
372
        if (!array_key_exists($relationName, $this->relationships)) {
373
            $this->relationships[$relationName] = new MorphToRelation();
374
        }
375 20
376
        return $this->relationships[$relationName];
377 20
    }
378 20
379
    /**
380 20
     * Create a plural relation to another item.
381 20
     *
382
     * @param string|null $relationName
383
     *
384 20
     * @return \Swis\JsonApi\Client\Relations\MorphToManyRelation
385
     */
386
    public function morphToMany(string $relationName = null): MorphToManyRelation
387
    {
388
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
389
390
        if (!array_key_exists($relationName, $this->relationships)) {
391
            $this->relationships[$relationName] = new MorphToManyRelation();
392
        }
393
394 50
        return $this->relationships[$relationName];
395
    }
396 50
397
    /**
398 50
     * Sets the initial values of an Item.
399 50
     *
400
     * @param array $initial
401
     *
402 50
     * @return static
403
     */
404
    public function setInitial(array $initial)
405
    {
406
        $this->initial = $initial;
407
408
        return $this;
409
    }
410
411
    /**
412 55
     * Returns the initial values of an Item.
413
     *
414 55
     * @param string|null $key
415
     *
416 55
     * @return array|mixed
417 55
     */
418
    public function getInitial($key = null)
419
    {
420 55
        if (null === $key) {
421
            return $this->initial;
422
        }
423
424
        return $this->initial[$key];
425
    }
426
427
    /**
428
     * @param string $key
429
     *
430 10
     * @return bool
431
     */
432 10
    public function hasInitial($key): bool
433
    {
434 10
        return isset($this->getInitial()[$key]);
435
    }
436
437
    /**
438
     * Prefills the model with values from $initial, when adding new item.
439
     *
440
     * @return static
441
     */
442
    public function useInitial()
443
    {
444 5
        $this->fill($this->initial);
445
446 5
        return $this;
447 5
    }
448
449
    /**
450
     * @return array
451
     */
452
    public function getAvailableRelations(): array
453
    {
454
        return $this->availableRelations;
455
    }
456
457
    /**
458
     * Set the specific relationship on the model.
459
     *
460
     * @param string                                        $relation
461
     * @param \Swis\JsonApi\Client\Interfaces\DataInterface $value
462
     *
463
     * @return static
464
     */
465
    public function setRelation(string $relation, DataInterface $value)
466
    {
467
        if (method_exists($this, $relation)) {
468 5
            /** @var \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface $relationObject */
469
            $relationObject = $this->$relation();
470 5
        } elseif ($value instanceof Collection) {
471
            $relationObject = $this->morphToMany(snake_case($relation));
472 5
        } else {
473
            $relationObject = $this->morphTo(snake_case($relation));
474
        }
475
476
        $relationObject->associate($value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type Swis\JsonApi\Client\Collection; however, parameter $included of Swis\JsonApi\Client\Inte...nInterface::associate() does only seem to accept Swis\JsonApi\Client\Interfaces\ItemInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

476
        $relationObject->associate(/** @scrutinizer ignore-type */ $value);
Loading history...
Bug introduced by
It seems like $value can also be of type Swis\JsonApi\Client\Collection; however, parameter $included of Swis\JsonApi\Client\Rela...neRelation::associate() does only seem to accept Swis\JsonApi\Client\Interfaces\ItemInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

476
        $relationObject->associate(/** @scrutinizer ignore-type */ $value);
Loading history...
477
478 105
        return $this;
479
    }
480
}
481