Completed
Push — master ( 60109a...527079 )
by Jasper
07:00
created

Item::hasMany()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

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