Passed
Pull Request — master (#51)
by Jasper
12:04
created

Item::__set()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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

287
        $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...
288 80
        if (method_exists($this, $method)) {
289
            return $this->$method()->getIncluded();
290
        }
291
292
        // If the "attribute" exists as a relationship on the model, we will return
293
        // the included items in the relationship
294
        if ($this->hasRelationship($key)) {
295
            return $this->getRelationship($key)->getIncluded();
296 25
        }
297
298 25
        return null;
299
    }
300
301
    /**
302
     * Determine if an attribute exists on the model.
303
     *
304
     * @param string $key
305
     *
306
     * @return bool
307
     */
308
    public function __isset($key)
309
    {
310
        if ($key === 'id') {
311
            return $this->hasId();
312
        }
313
314
        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

314
        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...
315
    }
316
317
    /**
318
     * @param string $name
319
     *
320
     * @return \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface
321 40
     */
322
    public function getRelationship(string $name)
323 40
    {
324
        return $this->relationships[$name];
325 40
    }
326 40
327
    /**
328
     * @param string $name
329 40
     *
330
     * @return bool
331
     */
332
    public function hasRelationship(string $name): bool
333
    {
334
        return array_key_exists($name, $this->relationships);
335
    }
336
337
    /**
338
     * @param $name
339
     *
340 20
     * @return static
341
     */
342 20
    public function removeRelationship(string $name)
343
    {
344 20
        unset($this->relationships[$name]);
345 20
346
        return $this;
347
    }
348 20
349
    /**
350
     * Create a singular relation to another item.
351
     *
352
     * @param string      $class
353
     * @param string|null $relationName
354
     *
355
     * @return \Swis\JsonApi\Client\Relations\HasOneRelation
356
     */
357
    public function hasOne(string $class, string $relationName = null)
358 50
    {
359
        $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

359
        $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...
360 50
361
        if (!array_key_exists($relationName, $this->relationships)) {
362 50
            $this->relationships[$relationName] = new HasOneRelation((new $class())->getType());
363 50
        }
364
365
        return $this->relationships[$relationName];
366 50
    }
367
368
    /**
369
     * Create a plural relation to another item.
370
     *
371
     * @param string      $class
372
     * @param string|null $relationName
373
     *
374
     * @return \Swis\JsonApi\Client\Relations\HasManyRelation
375
     */
376 55
    public function hasMany(string $class, string $relationName = null)
377
    {
378 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

378
        $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...
379
380 55
        if (!array_key_exists($relationName, $this->relationships)) {
381 55
            $this->relationships[$relationName] = new HasManyRelation((new $class())->getType());
382
        }
383
384 55
        return $this->relationships[$relationName];
385
    }
386
387
    /**
388
     * Create a singular relation to another item.
389
     *
390
     * @param string|null $relationName
391
     *
392
     * @return \Swis\JsonApi\Client\Relations\MorphToRelation
393
     */
394 10
    public function morphTo(string $relationName = null)
395
    {
396 10
        $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

396
        $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...
397
398 10
        if (!array_key_exists($relationName, $this->relationships)) {
399
            $this->relationships[$relationName] = new MorphToRelation();
400
        }
401
402
        return $this->relationships[$relationName];
403
    }
404
405
    /**
406
     * Create a plural relation to another item.
407
     *
408 5
     * @param string|null $relationName
409
     *
410 5
     * @return \Swis\JsonApi\Client\Relations\MorphToManyRelation
411 5
     */
412
    public function morphToMany(string $relationName = null)
413
    {
414
        $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

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

499
            $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...
500
        } else {
501
            $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

501
            $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...
502
        }
503
504
        $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

504
        $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

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