Completed
Push — master ( 318ca6...97a34f )
by Jasper
22s queued 10s
created

Item   F

Complexity

Total Complexity 65

Size/Duplication

Total Lines 472
Duplicated Lines 0 %

Test Coverage

Coverage 81.12%

Importance

Changes 0
Metric Value
wmc 65
eloc 127
dl 0
loc 472
ccs 116
cts 143
cp 0.8112
rs 3.2
c 0
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
A hasId() 0 3 1
A setType() 0 5 1
A toJsonApiArray() 0 21 4
A isNew() 0 3 1
A getType() 0 3 1
A setId() 0 5 1
A getId() 0 3 1
A setRelation() 0 16 3
B getIncluded() 0 32 10
A hasRelationship() 0 3 1
A __isset() 0 6 4
A removeRelationship() 0 5 1
A getInitial() 0 7 2
A hasInitial() 0 3 1
A useInitial() 0 5 1
A hasMany() 0 10 3
A setInitial() 0 5 1
A morphToMany() 0 9 3
A hasAttribute() 0 3 1
A getAvailableRelations() 0 3 1
A morphTo() 0 9 3
A getAttribute() 0 7 3
B getRelationships() 0 52 10
A hasOne() 0 10 3
A getRelationship() 0 3 1
A getRelationValue() 0 13 3

How to fix   Complexity   

Complex Class

Complex classes like Item often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Item, and based on these observations, apply Extract Interface, too.

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 80
    public function toJsonApiArray(): array
53
    {
54
        $data = [
55 80
            'type' => $this->getType(),
56
        ];
57
58 80
        if ($this->hasId()) {
59 65
            $data['id'] = $this->getId();
60
        }
61
62 80
        $attributes = $this->toArray();
63 80
        if (!empty($attributes)) {
64 15
            $data['attributes'] = $attributes;
65
        }
66
67 80
        $relationships = $this->getRelationships();
68 80
        if (!empty($relationships)) {
69 65
            $data['relationships'] = $relationships;
70
        }
71
72 80
        return $data;
73
    }
74
75
    /**
76
     * @return string
77
     */
78 165
    public function getType(): string
79
    {
80 165
        return $this->type;
81
    }
82
83
    /**
84
     * @param string $type
85
     *
86
     * @return static
87
     */
88 70
    public function setType(string $type)
89
    {
90 70
        $this->type = $type;
91
92 70
        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 100
    public function hasId(): bool
107
    {
108 100
        return isset($this->id);
109
    }
110
111
    /**
112
     * @return string
113
     */
114 205
    public function getId()
115
    {
116 205
        return $this->id;
117
    }
118
119
    /**
120
     * @param string $id
121
     *
122
     * @return static
123
     */
124 205
    public function setId($id)
125
    {
126 205
        $this->id = $id;
127
128 205
        return $this;
129
    }
130
131
    /**
132
     * @return array
133
     */
134 85
    public function getRelationships(): array
135
    {
136 85
        $relationships = [];
137
138
        /** @var \Swis\JsonApi\Client\Interfaces\RelationInterface $relationship */
139 85
        foreach ($this->relationships as $name => $relationship) {
140 70
            if ($relationship instanceof HasOneRelation) {
141 20
                $relationships[$name] = ['data' => null];
142
143 20
                if ($relationship->getIncluded() !== null) {
144 10
                    $relationships[$name] = [
145
                        'data' => [
146 10
                            'type' => $relationship->getType(),
147 20
                            'id'   => $relationship->getId(),
148
                        ],
149
                    ];
150
                }
151 50
            } 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 20
                            'id'   => $item->getId(),
159
                        ];
160
                }
161 30
            } elseif ($relationship instanceof MorphToRelation) {
162 15
                $relationships[$name] = ['data' => null];
163
164 15
                if ($relationship->getIncluded() !== null) {
165 10
                    $relationships[$name] = [
166
                        'data' => [
167 10
                            'type' => $relationship->getIncluded()->getType(),
168 15
                            'id'   => $relationship->getIncluded()->getId(),
169
                        ],
170
                    ];
171
                }
172 15
            } elseif ($relationship instanceof MorphToManyRelation) {
173 15
                $relationships[$name]['data'] = [];
174
175 15
                foreach ($relationship->getIncluded() as $item) {
176 10
                    $relationships[$name]['data'][] =
177
                        [
178 10
                            'type' => $item->getType(),
179 70
                            'id'   => $item->getId(),
180
                        ];
181
                }
182
            }
183
        }
184
185 85
        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 (!empty($includedFromRelationship->getType()) && null !== $includedFromRelationship->getId()) {
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 (!empty($item->getType()) && null !== $item->getId()) {
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 35
        return $included->unique(
225 21
            function (array $item) {
226
                return $item['type'].':'.$item['id'];
227 35
            }
228
        );
229
    }
230
231
    /**
232
     * @param string $key
233
     *
234
     * @return \Swis\JsonApi\Client\Interfaces\DataInterface|mixed
235
     */
236 55
    public function getAttribute($key)
237
    {
238 55
        if ($this->hasAttribute($key) || $this->hasGetMutator($key)) {
239 35
            return parent::getAttribute($key);
240
        }
241
242 20
        return $this->getRelationValue($key);
243
    }
244
245
    /**
246
     * @param string $key
247
     *
248
     * @return bool
249
     */
250 55
    public function hasAttribute($key)
251
    {
252 55
        return array_key_exists($key, $this->attributes);
253
    }
254
255
    /**
256
     * Get the relationship data.
257
     *
258
     * @param string $key
259
     *
260
     * @return \Swis\JsonApi\Client\Interfaces\DataInterface
261
     */
262 20
    public function getRelationValue($key)
263
    {
264
        // 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 20
        $method = camel_case($key);
267 20
        if (method_exists($this, $method)) {
268 10
            return $this->$method()->getIncluded();
269
        }
270
271
        // If the "attribute" exists as a relationship on the model, we will return
272
        // the included items in the relationship
273 10
        if ($this->hasRelationship($key)) {
274 10
            return $this->getRelationship($key)->getIncluded();
275
        }
276
    }
277
278
    /**
279
     * Determine if an attribute exists on the model.
280
     *
281
     * @param string $key
282
     *
283
     * @return bool
284
     */
285
    public function __isset($key)
286
    {
287
        $result = (isset($this->attributes[$key]) || isset($this->relationships[snake_case($key)])) ||
288
            ($this->hasGetMutator($key) && !is_null($this->getAttributeValue($key)));
289
290
        return $result;
291
    }
292
293
    /**
294
     * @param $name
295
     *
296
     * @return \Swis\JsonApi\Client\Interfaces\RelationInterface
297
     */
298 65
    public function getRelationship(string $name): RelationInterface
299
    {
300 65
        return $this->relationships[$name];
301
    }
302
303
    /**
304
     * @param string $name
305
     *
306
     * @return bool
307
     */
308 25
    public function hasRelationship(string $name): bool
309
    {
310 25
        return array_key_exists($name, $this->relationships);
311
    }
312
313
    /**
314
     * @param $name
315
     *
316
     * @return static
317
     */
318
    public function removeRelationship(string $name)
319
    {
320
        unset($this->relationships[$name]);
321
322
        return $this;
323
    }
324
325
    /**
326
     * Create a singular relation to another item.
327
     *
328
     * @param string      $class
329
     * @param string|null $relationName
330
     *
331
     * @return \Swis\JsonApi\Client\Relations\HasOneRelation
332
     */
333 40
    public function hasOne(string $class, string $relationName = null)
334
    {
335 40
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
336 40
        $itemType = (new $class())->getType();
337
338 40
        if (!array_key_exists($relationName, $this->relationships)) {
339 40
            $this->relationships[$relationName] = new HasOneRelation($itemType);
340
        }
341
342 40
        return $this->relationships[$relationName];
343
    }
344
345
    /**
346
     * Create a plural relation to another item.
347
     *
348
     * @param string      $class
349
     * @param string|null $relationName
350
     *
351
     * @return \Swis\JsonApi\Client\Relations\HasManyRelation
352
     */
353 20
    public function hasMany(string $class, string $relationName = null)
354
    {
355 20
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
356 20
        $itemType = (new $class())->getType();
357
358 20
        if (!array_key_exists($relationName, $this->relationships)) {
359 20
            $this->relationships[$relationName] = new HasManyRelation($itemType);
360
        }
361
362 20
        return $this->relationships[$relationName];
363
    }
364
365
    /**
366
     * Create a singular relation to another item.
367
     *
368
     * @param string|null $relationName
369
     *
370
     * @return \Swis\JsonApi\Client\Relations\MorphToRelation
371
     */
372 45
    public function morphTo(string $relationName = null)
373
    {
374 45
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
375
376 45
        if (!array_key_exists($relationName, $this->relationships)) {
377 45
            $this->relationships[$relationName] = new MorphToRelation();
378
        }
379
380 45
        return $this->relationships[$relationName];
381
    }
382
383
    /**
384
     * Create a plural relation to another item.
385
     *
386
     * @param string|null $relationName
387
     *
388
     * @return \Swis\JsonApi\Client\Relations\MorphToManyRelation
389
     */
390 50
    public function morphToMany(string $relationName = null)
391
    {
392 50
        $relationName = $relationName ?: snake_case(debug_backtrace()[1]['function']);
393
394 50
        if (!array_key_exists($relationName, $this->relationships)) {
395 50
            $this->relationships[$relationName] = new MorphToManyRelation();
396
        }
397
398 50
        return $this->relationships[$relationName];
399
    }
400
401
    /**
402
     * Sets the initial values of an Item.
403
     *
404
     * @param array $initial
405
     *
406
     * @return static
407
     */
408 10
    public function setInitial(array $initial)
409
    {
410 10
        $this->initial = $initial;
411
412 10
        return $this;
413
    }
414
415
    /**
416
     * Returns the initial values of an Item.
417
     *
418
     * @param string|null $key
419
     *
420
     * @return array|mixed
421
     */
422 5
    public function getInitial($key = null)
423
    {
424 5
        if (null === $key) {
425 5
            return $this->initial;
426
        }
427
428
        return $this->initial[$key];
429
    }
430
431
    /**
432
     * @param string $key
433
     *
434
     * @return bool
435
     */
436
    public function hasInitial($key): bool
437
    {
438
        return isset($this->getInitial()[$key]);
439
    }
440
441
    /**
442
     * Prefills the model with values from $initial, when adding new item.
443
     *
444
     * @return static
445
     */
446 5
    public function useInitial()
447
    {
448 5
        $this->fill($this->initial);
449
450 5
        return $this;
451
    }
452
453
    /**
454
     * @return array
455
     */
456 90
    public function getAvailableRelations(): array
457
    {
458 90
        return $this->availableRelations;
459
    }
460
461
    /**
462
     * Set the specific relationship in the model.
463
     *
464
     * @param string $relation
465
     * @param mixed  $value
466
     *
467
     * @return static
468
     */
469 30
    public function setRelation($relation, $value)
470
    {
471 30
        if (method_exists($this, $relation)) {
472
            /** @var \Swis\JsonApi\Client\Interfaces\RelationInterface $relationObject */
473 15
            $relationObject = $this->$relation();
474
        } else {
475 20
            if ($value instanceof Collection) {
476 10
                $relationObject = $this->morphToMany(snake_case($relation));
477
            } else {
478 15
                $relationObject = $this->morphTo(snake_case($relation));
479
            }
480
        }
481
482 30
        $relationObject->associate($value);
483
484 30
        return $this;
485
    }
486
}
487