Completed
Push — master ( 6972d7...5fe147 )
by Jasper
05:44
created

Item::getRelationship()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

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

468
        $relationObject->associate(/** @scrutinizer ignore-type */ $value);
Loading history...
469 30
        $relationObject->setLinks($links);
470 30
        $relationObject->setMeta($meta);
471
472 30
        return $this;
473
    }
474
}
475