Item::__construct()   A
last analyzed

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