Completed
Push — master ( ba7fd9...695cd0 )
by Jasper
24s
created

Item::__get()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
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
use Swis\JsonApi\Client\Traits\HasLinks;
15
use Swis\JsonApi\Client\Traits\HasMeta;
16
use Swis\JsonApi\Client\Traits\HasType;
17
18
/**
19
 * @property string|null id
20
 */
21
class Item extends Model implements ItemInterface
22
{
23
    use HasLinks;
24
    use HasMeta;
25
    use HasType;
26
27
    /**
28
     * @var string|null
29
     */
30
    protected $id;
31
32
    /**
33
     * Contains the initial values.
34
     *
35
     * @var array
36
     */
37
    protected $initial = [];
38
39
    /**
40
     * @var \Swis\JsonApi\Client\Interfaces\OneRelationInterface[]|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface[]
41
     */
42
    protected $relationships = [];
43
44
    /**
45
     * Available relations need to be explicitly set.
46
     *
47
     * @var array
48
     */
49
    protected $availableRelations = [];
50
51
    /**
52
     * @var array
53
     */
54
    protected $guarded = ['id'];
55
56
    /**
57
     * @return array
58
     */
59 115
    public function toJsonApiArray(): array
60
    {
61
        $data = [
62 115
            'type' => $this->getType(),
63
        ];
64
65 115
        if ($this->hasId()) {
66 70
            $data['id'] = $this->getId();
67
        }
68
69 115
        $attributes = $this->toArray();
70 115
        if (!empty($attributes)) {
71 10
            $data['attributes'] = $attributes;
72
        }
73
74 115
        $relationships = $this->getRelationships();
75 115
        if (!empty($relationships)) {
76 80
            $data['relationships'] = $relationships;
77
        }
78
79 115
        $links = $this->getLinks();
80 115
        if ($links !== null) {
81 5
            $data['links'] = $links->toArray();
82
        }
83
84 115
        $meta = $this->getMeta();
85 115
        if ($meta !== null) {
86 5
            $data['meta'] = $meta->toArray();
87
        }
88
89 115
        return $data;
90
    }
91
92
    /**
93
     * @return bool
94
     */
95 15
    public function isNew(): bool
96
    {
97 15
        return !$this->hasId();
98
    }
99
100
    /**
101
     * @return bool
102
     */
103 140
    public function hasId(): bool
104
    {
105 140
        return isset($this->id);
106
    }
107
108
    /**
109
     * @return string|null
110
     */
111 240
    public function getId(): ? string
112
    {
113 240
        return $this->id;
114
    }
115
116
    /**
117
     * @param string|null $id
118
     *
119
     * @return static
120
     */
121 260
    public function setId(? string $id)
122
    {
123 260
        $this->id = $id;
124
125 260
        return $this;
126
    }
127
128
    /**
129
     * @return array
130
     */
131 120
    public function getRelationships(): array
132
    {
133 120
        $relationships = [];
134
135 120
        foreach ($this->relationships as $name => $relationship) {
136 85
            if ($relationship instanceof OneRelationInterface) {
137 45
                $relationships[$name] = ['data' => null];
138
139 45
                if ($relationship->getIncluded() !== null) {
140 35
                    $relationships[$name] = [
141
                        'data' => [
142 35
                            'type' => $relationship->getIncluded()->getType(),
143 45
                            'id'   => $relationship->getIncluded()->getId(),
144
                        ],
145
                    ];
146
                }
147 40
            } elseif ($relationship instanceof ManyRelationInterface) {
148 40
                $relationships[$name]['data'] = [];
149
150 40
                foreach ($relationship->getIncluded() as $item) {
151 30
                    $relationships[$name]['data'][] =
152
                        [
153 30
                            'type' => $item->getType(),
154 41
                            'id'   => $item->getId(),
155
                        ];
156
                }
157
            }
158
        }
159
160 120
        return $relationships;
161
    }
162
163
    /**
164
     * @TODO: MEGA TODO. Set up a serializer for the Item so that we can remove this, getRelationships etc
165
     *
166
     * @return \Swis\JsonApi\Client\Collection
167
     */
168 35
    public function getIncluded(): Collection
169
    {
170 35
        $included = new Collection();
171
172 35
        foreach ($this->relationships as $name => $relationship) {
173
            if ($relationship->shouldOmitIncluded() || !$relationship->hasIncluded()) {
174
                continue;
175
            }
176
177
            if ($relationship instanceof OneRelationInterface) {
178
                /** @var \Swis\JsonApi\Client\Interfaces\ItemInterface $item */
179
                $item = $relationship->getIncluded();
180
                if ($item->canBeIncluded()) {
181
                    $included->push($item->toJsonApiArray());
182
                }
183
                $included = $included->merge($item->getIncluded());
184
            } elseif ($relationship instanceof ManyRelationInterface) {
185
                $relationship->getIncluded()->each(
186
                    function (ItemInterface $item) use (&$included) {
187
                        if ($item->canBeIncluded()) {
188
                            $included->push($item->toJsonApiArray());
189
                        }
190
                        $included = $included->merge($item->getIncluded());
191
                    }
192
                );
193
            }
194
        }
195
196
        return $included
197 35
            ->unique(
198 14
                function (array $item) {
199
                    return $item['type'].':'.$item['id'];
200 35
                }
201
            )
202 35
            ->values();
203
    }
204
205
    /**
206
     * @return bool
207
     */
208
    public function canBeIncluded(): bool
209
    {
210
        if (empty($this->getType())) {
211
            return false;
212
        }
213
214
        if (null === $this->getId()) {
215
            return false;
216
        }
217
218
        if (empty($this->relationships) && empty($this->toArray())) {
219
            return false;
220
        }
221
222
        return true;
223
    }
224
225
    /**
226
     * @param string $key
227
     *
228
     * @return mixed
229
     */
230 35
    public function __get($key)
231
    {
232 35
        if ($key === 'id') {
233 5
            return $this->getId();
234
        }
235
236 30
        return parent::__get($key);
237
    }
238
239
    /**
240
     * @param string $key
241
     *
242
     * @return \Swis\JsonApi\Client\Interfaces\DataInterface|mixed
243
     */
244 55
    public function getAttribute($key)
245
    {
246 55
        if ($this->hasAttribute($key) || $this->hasGetMutator($key)) {
247 35
            return parent::getAttribute($key);
248
        }
249
250 20
        return $this->getRelationValue($key);
251
    }
252
253
    /**
254
     * @param string $key
255
     *
256
     * @return bool
257
     */
258 55
    public function hasAttribute($key): bool
259
    {
260 55
        return array_key_exists($key, $this->attributes);
261
    }
262
263
    /**
264
     * @param string $key
265
     * @param mixed  $value
266
     */
267 5
    public function __set($key, $value)
268
    {
269 5
        if ($key === 'id') {
270 5
            $this->setId($value);
271
272 5
            return;
273
        }
274
275
        parent::__set($key, $value);
276
    }
277
278
    /**
279
     * Get the relationship data.
280
     *
281
     * @param string $key
282
     *
283
     * @return \Swis\JsonApi\Client\Interfaces\DataInterface|null
284
     */
285 20
    public function getRelationValue($key)
286
    {
287
        // If the "attribute" exists as a method on the model, we will just assume
288
        // it is a relationship and will load and return the included items in the relationship
289 20
        $method = camel_case($key);
0 ignored issues
show
Deprecated Code introduced by
The function camel_case() has been deprecated: Str::camel() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

289
        $method = /** @scrutinizer ignore-deprecated */ camel_case($key);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
290 20
        if (method_exists($this, $method)) {
291 10
            return $this->$method()->getIncluded();
292
        }
293
294
        // If the "attribute" exists as a relationship on the model, we will return
295
        // the included items in the relationship
296 10
        if ($this->hasRelationship($key)) {
297 10
            return $this->getRelationship($key)->getIncluded();
298
        }
299
300
        return null;
301
    }
302
303
    /**
304
     * Determine if an attribute exists on the model.
305
     *
306
     * @param string $key
307
     *
308
     * @return bool
309
     */
310 5
    public function __isset($key)
311
    {
312 5
        if ($key === 'id') {
313 5
            return $this->hasId();
314
        }
315
316
        return parent::__isset($key) || $this->hasRelationship($key) || $this->hasRelationship(snake_case($key));
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

316
        return parent::__isset($key) || $this->hasRelationship($key) || $this->hasRelationship(/** @scrutinizer ignore-deprecated */ snake_case($key));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
317
    }
318
319
    /**
320
     * @param string $name
321
     *
322
     * @return \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface
323
     */
324 80
    public function getRelationship(string $name)
325
    {
326 80
        return $this->relationships[$name];
327
    }
328
329
    /**
330
     * @param string $name
331
     *
332
     * @return bool
333
     */
334 25
    public function hasRelationship(string $name): bool
335
    {
336 25
        return array_key_exists($name, $this->relationships);
337
    }
338
339
    /**
340
     * @param $name
341
     *
342
     * @return static
343
     */
344
    public function removeRelationship(string $name)
345
    {
346
        unset($this->relationships[$name]);
347
348
        return $this;
349
    }
350
351
    /**
352
     * Create a singular relation to another item.
353
     *
354
     * @param string      $class
355
     * @param string|null $relationName
356
     *
357
     * @return \Swis\JsonApi\Client\Relations\HasOneRelation
358
     */
359 40
    public function hasOne(string $class, string $relationName = null)
360
    {
361 40
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

361
        $relationName = $relationName ?: /** @scrutinizer ignore-deprecated */ snake_case(debug_backtrace()[1]['function']);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
362
363 40
        if (!array_key_exists($relationName, $this->relationships)) {
364 40
            $this->relationships[$relationName] = new HasOneRelation((new $class())->getType());
365
        }
366
367 40
        return $this->relationships[$relationName];
368
    }
369
370
    /**
371
     * Create a plural relation to another item.
372
     *
373
     * @param string      $class
374
     * @param string|null $relationName
375
     *
376
     * @return \Swis\JsonApi\Client\Relations\HasManyRelation
377
     */
378 20
    public function hasMany(string $class, string $relationName = null)
379
    {
380 20
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

380
        $relationName = $relationName ?: /** @scrutinizer ignore-deprecated */ snake_case(debug_backtrace()[1]['function']);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
381
382 20
        if (!array_key_exists($relationName, $this->relationships)) {
383 20
            $this->relationships[$relationName] = new HasManyRelation((new $class())->getType());
384
        }
385
386 20
        return $this->relationships[$relationName];
387
    }
388
389
    /**
390
     * Create a singular relation to another item.
391
     *
392
     * @param string|null $relationName
393
     *
394
     * @return \Swis\JsonApi\Client\Relations\MorphToRelation
395
     */
396 50
    public function morphTo(string $relationName = null)
397
    {
398 50
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

398
        $relationName = $relationName ?: /** @scrutinizer ignore-deprecated */ snake_case(debug_backtrace()[1]['function']);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
399
400 50
        if (!array_key_exists($relationName, $this->relationships)) {
401 50
            $this->relationships[$relationName] = new MorphToRelation();
402
        }
403
404 50
        return $this->relationships[$relationName];
405
    }
406
407
    /**
408
     * Create a plural relation to another item.
409
     *
410
     * @param string|null $relationName
411
     *
412
     * @return \Swis\JsonApi\Client\Relations\MorphToManyRelation
413
     */
414 55
    public function morphToMany(string $relationName = null)
415
    {
416 55
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

416
        $relationName = $relationName ?: /** @scrutinizer ignore-deprecated */ snake_case(debug_backtrace()[1]['function']);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
417
418 55
        if (!array_key_exists($relationName, $this->relationships)) {
419 55
            $this->relationships[$relationName] = new MorphToManyRelation();
420
        }
421
422 55
        return $this->relationships[$relationName];
423
    }
424
425
    /**
426
     * Sets the initial values of an Item.
427
     *
428
     * @param array $initial
429
     *
430
     * @return static
431
     */
432 10
    public function setInitial(array $initial)
433
    {
434 10
        $this->initial = $initial;
435
436 10
        return $this;
437
    }
438
439
    /**
440
     * Returns the initial values of an Item.
441
     *
442
     * @param string|null $key
443
     *
444
     * @return array|mixed
445
     */
446 5
    public function getInitial($key = null)
447
    {
448 5
        if (null === $key) {
449 5
            return $this->initial;
450
        }
451
452
        return $this->initial[$key];
453
    }
454
455
    /**
456
     * @param string $key
457
     *
458
     * @return bool
459
     */
460
    public function hasInitial($key): bool
461
    {
462
        return isset($this->getInitial()[$key]);
463
    }
464
465
    /**
466
     * Prefills the model with values from $initial, when adding new item.
467
     *
468
     * @return static
469
     */
470 5
    public function useInitial()
471
    {
472 5
        $this->fill($this->initial);
473
474 5
        return $this;
475
    }
476
477
    /**
478
     * @return array
479
     */
480 105
    public function getAvailableRelations(): array
481
    {
482 105
        return $this->availableRelations;
483
    }
484
485
    /**
486
     * Set the specific relationship on the model.
487
     *
488
     * @param string                                        $relation
489
     * @param \Swis\JsonApi\Client\Interfaces\DataInterface $value
490
     * @param \Swis\JsonApi\Client\Links|null               $links
491
     * @param \Swis\JsonApi\Client\Meta|null                $meta
492
     *
493
     * @return static
494
     */
495 30
    public function setRelation(string $relation, DataInterface $value, Links $links = null, Meta $meta = null)
496
    {
497 30
        if (method_exists($this, $relation)) {
498
            /** @var \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface $relationObject */
499 15
            $relationObject = $this->$relation();
500 20
        } elseif ($value instanceof Collection) {
501 10
            $relationObject = $this->morphToMany(snake_case($relation));
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

501
            $relationObject = $this->morphToMany(/** @scrutinizer ignore-deprecated */ snake_case($relation));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
502
        } else {
503 15
            $relationObject = $this->morphTo(snake_case($relation));
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated: Str::snake() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

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

503
            $relationObject = $this->morphTo(/** @scrutinizer ignore-deprecated */ snake_case($relation));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
504
        }
505
506 30
        $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\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

506
        $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\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

506
        $relationObject->associate(/** @scrutinizer ignore-type */ $value);
Loading history...
507 30
        $relationObject->setLinks($links);
508 30
        $relationObject->setMeta($meta);
509
510 30
        return $this;
511
    }
512
}
513