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

Item::canBeIncluded()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 11.1035

Importance

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

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

477
        $relationObject->/** @scrutinizer ignore-call */ 
478
                         associate($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

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