Passed
Push — master ( 975c2f...d51421 )
by Jasper
13:00 queued 01:21
created

Item   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 420
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 105
dl 0
loc 420
ccs 135
cts 135
cp 1
rs 8.4
c 3
b 0
f 0
wmc 50

26 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A jsonSerialize() 0 3 1
A hasAttributes() 0 3 1
A offsetGet() 0 7 2
A replicate() 0 5 1
A toArray() 0 3 1
A __isset() 0 3 1
A __toString() 0 3 1
A useInitial() 0 5 1
A hydrate() 0 7 1
A __get() 0 3 1
A offsetExists() 0 7 2
A offsetUnset() 0 7 2
A forceFill() 0 4 1
A toJsonApiArray() 0 31 6
A newInstance() 0 10 2
A toJson() 0 3 1
A hasAttribute() 0 3 1
A fill() 0 16 4
A isNew() 0 3 1
A getAvailableRelations() 0 3 1
A __unset() 0 3 1
B getRelationships() 0 48 11
A hasRelationships() 0 3 1
A __set() 0 3 1
A offsetSet() 0 9 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
declare(strict_types=1);
4
5
namespace Swis\JsonApi\Client;
6
7
use ArrayAccess;
8
use Illuminate\Contracts\Support\Arrayable;
9
use Illuminate\Contracts\Support\Jsonable;
10
use JsonSerializable;
11
use Swis\JsonApi\Client\Exceptions\MassAssignmentException;
12
use Swis\JsonApi\Client\Interfaces\ItemInterface;
13
use Swis\JsonApi\Client\Interfaces\ManyRelationInterface;
14
use Swis\JsonApi\Client\Interfaces\OneRelationInterface;
15
16
/**
17
 * @property string|null id
18
 */
19
class Item implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, ItemInterface
20
{
21 1
    use Concerns\GuardsAttributes;
22 1
    use Concerns\HasAttributes;
23 1
    use Concerns\HasId;
24 1
    use Concerns\HasInitial;
25 1
    use Concerns\HasLinks;
26 1
    use Concerns\HasMeta;
27 1
    use Concerns\HasRelations;
28 1
    use Concerns\HasType;
29 1
    use Concerns\HidesAttributes;
30
31
    /**
32
     * Available relations need to be explicitly set.
33
     *
34
     * @var array
35
     */
36
    protected $availableRelations = [];
37
38
    /**
39
     * Create a new Item instance.
40
     *
41
     * @param array $attributes
42
     *
43
     * @return void
44
     */
45 513
    public function __construct(array $attributes = [])
46
    {
47 513
        $this->fill($attributes);
48 513
    }
49
50
    /**
51
     * Fill the model with an array of attributes.
52
     *
53
     * @param array $attributes
54
     *
55
     * @throws \Swis\JsonApi\Client\Exceptions\MassAssignmentException
56
     *
57
     * @return $this
58
     */
59 513
    public function fill(array $attributes)
60
    {
61 513
        $totallyGuarded = $this->totallyGuarded();
62
63 513
        foreach ($this->fillableFromArray($attributes) as $key => $value) {
64
            // The developers may choose to place some attributes in the "fillable" array
65
            // which means only those attributes may be set through mass assignment to
66
            // the model, and all others will just get ignored for security reasons.
67 177
            if ($this->isFillable($key)) {
68 150
                $this->setAttribute($key, $value);
69 39
            } elseif ($totallyGuarded) {
70 3
                throw new MassAssignmentException(sprintf('Add [%s] to fillable property to allow mass assignment on [%s].', $key, get_class($this)));
71
            }
72
        }
73
74 513
        return $this;
75
    }
76
77
    /**
78
     * Fill the model with an array of attributes. Force mass assignment.
79
     *
80
     * @param array $attributes
81
     *
82
     * @return $this
83
     */
84 3
    public function forceFill(array $attributes)
85
    {
86 3
        return static::unguarded(function () use ($attributes) {
87 3
            return $this->fill($attributes);
88 3
        });
89
    }
90
91
    /**
92
     * Create a new instance of the given model.
93
     *
94
     * @param array $attributes
95
     *
96
     * @return static
97
     */
98 6
    public function newInstance(array $attributes = [])
99
    {
100 6
        $model = new static($attributes);
101
102 6
        if ($this->type) {
103 3
            $model->setType($this->type);
104
        }
105 6
        $model->mergeCasts($this->casts);
106
107 6
        return $model;
108
    }
109
110
    /**
111
     * Create a list of models from plain arrays.
112
     *
113
     * @param array $items
114
     *
115
     * @return array
116
     */
117 3
    public static function hydrate(array $items): array
118
    {
119 3
        $instance = new static();
120
121 3
        return array_map(static function ($item) use ($instance) {
122 3
            return $instance->newInstance($item);
123 3
        }, $items);
124
    }
125
126
    /**
127
     * @return array
128
     */
129 96
    public function toJsonApiArray(): array
130
    {
131 32
        $data = [
132 96
            'type' => $this->getType(),
133
        ];
134
135 96
        if ($this->hasId()) {
136 54
            $data['id'] = $this->getId();
137
        }
138
139 96
        $attributes = $this->toArray();
140 96
        if (!empty($attributes)) {
141 6
            $data['attributes'] = $attributes;
142
        }
143
144 96
        $relationships = $this->getRelationships();
145 96
        if (!empty($relationships)) {
146 63
            $data['relationships'] = $relationships;
147
        }
148
149 96
        $links = $this->getLinks();
150 96
        if ($links !== null) {
151 3
            $data['links'] = $links->toArray();
152
        }
153
154 96
        $meta = $this->getMeta();
155 96
        if ($meta !== null) {
156 3
            $data['meta'] = $meta->toArray();
157
        }
158
159 96
        return $data;
160
    }
161
162
    /**
163
     * Convert the model instance to JSON.
164
     *
165
     * @param int $options
166
     *
167
     * @return string
168
     */
169 3
    public function toJson($options = 0)
170
    {
171 3
        return json_encode($this->jsonSerialize(), JSON_THROW_ON_ERROR | $options);
172
    }
173
174
    /**
175
     * Convert the object into something JSON serializable.
176
     *
177
     * @return array
178
     */
179 6
    public function jsonSerialize()
180
    {
181 6
        return $this->toArray();
182
    }
183
184
    /**
185
     * Convert the model instance to an array.
186
     *
187
     * @return array
188
     */
189 135
    public function toArray()
190
    {
191 135
        return $this->attributesToArray();
192
    }
193
194
    /**
195
     * Clone the model into a new, non-existing instance.
196
     *
197
     * @param array|null $except
198
     *
199
     * @return static
200
     */
201 3
    public function replicate(array $except = null)
202
    {
203 3
        $attributes = Util::arrayExcept($this->getAttributes(), $except ?? []);
204
205 3
        return new static($attributes);
206
    }
207
208
    /**
209
     * @return bool
210
     */
211 9
    public function isNew(): bool
212
    {
213 9
        return !$this->hasId();
214
    }
215
216
    /**
217
     * @return bool
218
     */
219 24
    public function hasAttributes(): bool
220
    {
221 24
        return !empty($this->toArray());
222
    }
223
224
    /**
225
     * @param string $key
226
     *
227
     * @return bool
228
     */
229 120
    public function hasAttribute($key): bool
230
    {
231 120
        return array_key_exists($key, $this->attributes);
232
    }
233
234
    /**
235
     * @return array
236
     */
237 75
    public function getAvailableRelations(): array
238
    {
239 75
        return $this->availableRelations;
240
    }
241
242
    /**
243
     * @return array
244
     */
245 108
    public function getRelationships(): array
246
    {
247 108
        $relationships = [];
248
249 108
        foreach ($this->getRelations() as $name => $relation) {
250 84
            if (!$relation->hasIncluded()) {
251 12
                continue;
252
            }
253
254 72
            if ($relation instanceof OneRelationInterface) {
255 42
                $relationships[$name]['data'] = null;
256
257 42
                if ($relation->getIncluded() !== null) {
258 30
                    $relationships[$name]['data'] = [
259 30
                        'type' => $relation->getIncluded()->getType(),
260 30
                        'id' => $relation->getIncluded()->getId(),
261
                    ];
262 30
                    if ($relation->getIncluded()->getMeta()) {
263 42
                        $relationships[$name]['data']['meta'] = $relation->getIncluded()->getMeta()->toArray();
264
                    }
265
                }
266 33
            } elseif ($relation instanceof ManyRelationInterface) {
267 33
                $relationships[$name]['data'] = [];
268
269 33
                foreach ($relation->getIncluded() as $item) {
270 7
                    $data = [
271 21
                        'type' => $item->getType(),
272 21
                        'id' => $item->getId(),
273
                    ];
274 21
                    if ($item->getMeta()) {
275 3
                        $data['meta'] = $item->getMeta()->toArray();
276
                    }
277 21
                    $relationships[$name]['data'][] = $data;
278
                }
279
            }
280
281 72
            $links = $relation->getLinks();
282 72
            if ($links !== null) {
283 12
                $relationships[$name]['links'] = $links->toArray();
284
            }
285
286 72
            $meta = $relation->getMeta();
287 72
            if ($meta !== null) {
288 12
                $relationships[$name]['meta'] = $meta->toArray();
289
            }
290
        }
291
292 108
        return $relationships;
293
    }
294
295
    /**
296
     * @return bool
297
     */
298 9
    public function hasRelationships(): bool
299
    {
300 9
        return !empty($this->getRelationships());
301
    }
302
303
    /**
304
     * Fills the model with the values from $initial. This is useful for setting defaults when creating a new item.
305
     *
306
     * @return static
307
     */
308 3
    public function useInitial()
309
    {
310 3
        $this->fill($this->initial);
311
312 3
        return $this;
313
    }
314
315
    /**
316
     * Dynamically retrieve attributes on the model.
317
     *
318
     * @param string $key
319
     *
320
     * @return mixed
321
     */
322 36
    public function __get(string $key)
323
    {
324 36
        return $this->offsetGet($key);
325
    }
326
327
    /**
328
     * Dynamically set attributes on the model.
329
     *
330
     * @param string $key
331
     * @param mixed  $value
332
     *
333
     * @return void
334
     */
335 51
    public function __set(string $key, $value)
336
    {
337 51
        $this->offsetSet($key, $value);
338 51
    }
339
340
    /**
341
     * Determine if the given attribute exists.
342
     *
343
     * @param mixed $offset
344
     *
345
     * @return bool
346
     */
347 9
    public function offsetExists($offset)
348
    {
349 9
        if ($offset === 'id') {
350 3
            return $this->hasId();
351
        }
352
353 6
        return !is_null($this->getAttribute($offset));
354
    }
355
356
    /**
357
     * Get the value for a given offset.
358
     *
359
     * @param mixed $offset
360
     *
361
     * @return mixed
362
     */
363 36
    public function offsetGet($offset)
364
    {
365 36
        if ($offset === 'id') {
366 3
            return $this->getId();
367
        }
368
369 33
        return $this->getAttribute($offset);
370
    }
371
372
    /**
373
     * Set the value for a given offset.
374
     *
375
     * @param mixed $offset
376
     * @param mixed $value
377
     *
378
     * @return void
379
     */
380 51
    public function offsetSet($offset, $value)
381
    {
382 51
        if ($offset === 'id') {
383 6
            $this->setId($value ? (string) $value : null);
384
385 6
            return;
386
        }
387
388 45
        $this->setAttribute($offset, $value);
389 45
    }
390
391
    /**
392
     * Unset the value for a given offset.
393
     *
394
     * @param mixed $offset
395
     *
396
     * @return void
397
     */
398 9
    public function offsetUnset($offset)
399
    {
400 9
        if ($offset === 'id') {
401 3
            $this->setId(null);
402
        }
403
404 9
        unset($this->attributes[$offset], $this->relations[$offset]);
405 9
    }
406
407
    /**
408
     * Determine if an attribute or relation exists on the model.
409
     *
410
     * @param string $key
411
     *
412
     * @return bool
413
     */
414 9
    public function __isset(string $key)
415
    {
416 9
        return $this->offsetExists($key);
417
    }
418
419
    /**
420
     * Unset an attribute on the model.
421
     *
422
     * @param string $key
423
     *
424
     * @return void
425
     */
426 9
    public function __unset(string $key)
427
    {
428 9
        $this->offsetUnset($key);
429 9
    }
430
431
    /**
432
     * Convert the model to its string representation.
433
     *
434
     * @return string
435
     */
436 3
    public function __toString()
437
    {
438 3
        return $this->toJson();
439
    }
440
}
441