HasAttributes::getAttribute()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 6.8476

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 13
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 25
ccs 7
cts 16
cp 0.4375
crap 6.8476
rs 9.8333
1
<?php
2
3
namespace Ronanchilvers\Orm\Features;
4
5
use Ronanchilvers\Orm\Features\Type\ArrayHandler;
6
use Ronanchilvers\Orm\Features\Type\DateTimeHandler;
7
use Ronanchilvers\Orm\Features\Type\HandlerInterface;
8
use Ronanchilvers\Orm\Features\Type\IntHandler;
9
use Ronanchilvers\Orm\Features\Type\ModelHandler;
10
use Ronanchilvers\Utility\Str;
11
use RuntimeException;
12
13
/**
14
 * Feature trait for handling attributes
15
 *
16
 * @author Ronan Chilvers <[email protected]>
17
 */
18
trait HasAttributes
19
{
20
    /**
21
     * @var Ronanchilvers\Orm\Features\Type\HandlerInterface[]
22
     */
23
    static protected $typeHandlers = [
24
        'array'    => ArrayHandler::class,
25
        'datetime' => DateTimeHandler::class,
26
        'model'    => ModelHandler::class,
27
        'int'      => IntHandler::class,
28
    ];
29
30
    /**
31
     * @var array
32
     */
33
    protected $data = [];
34
35
    /**
36
     * @var array
37
     */
38
    protected $oldData = [];
39
40
    /**
41
     * @var array
42
     */
43
    protected $types = [];
44
45
    /************************************/
46
    /* Attribute getters / setters ******/
47
48
    /**
49
     * Get all the attributes for this model
50
     *
51
     * @return array
52
     * @author Ronan Chilvers <[email protected]>
53
     */
54
    public function getAttributes()
55
    {
56
        return $this->data;
57
    }
58
59
    /**
60
     * Does this model have a given attribute?
61
     *
62
     * @param string $attribute
63
     * @return boolean
64
     * @author Ronan Chilvers <[email protected]>
65
     */
66 1
    public function hasAttribute($attribute)
67
    {
68 1
        $attribute = static::prefix(
69 1
            Str::snake($attribute)
70 1
        );
71
72 1
        return array_key_exists($attribute, $this->data);
73
    }
74
75
    /**
76
     * Get the value of a given attribute
77
     *
78
     * @param string $attribute
79
     * @return mixed
80
     * @author Ronan Chilvers <[email protected]>
81
     */
82 1
    public function getAttribute($attribute)
83
    {
84 1
        if (!$this->hasAttribute($attribute)) {
85
            return null;
86
        }
87 1
        $attribute = Str::snake($attribute);
88 1
        $value = $this->data[static::prefix($attribute)];
89
90
        // Handle mutators here
91 1
        if ($this->hasGetMutator($attribute)) {
92
            return $this->getAttributeMutated(
93
                $attribute,
94
                $value
95
            );
96
        }
97
98
        // Handle types here
99 1
        if ($this->hasType($attribute)) {
100
            return $this->getAttributeToType(
101
                $attribute,
102
                $value
103
            );
104
        }
105
106 1
        return $value;
107
    }
108
109
    /**
110
     * Get the raw value of a given attribute without any transformation
111
     *
112
     * @param string $attribute
113
     * @return mixed
114
     * @author Ronan Chilvers <[email protected]>
115
     */
116
    public function getAttributeRaw($attribute)
117
    {
118
        if (!$this->hasAttribute($attribute)) {
119
            return null;
120
        }
121
        $attribute = Str::snake($attribute);
122
123
        return $this->data[static::prefix($attribute)];
124
    }
125
126
    /**
127
     * Get the value of a given attribute
128
     *
129
     * @param string $attribute
130
     * @param mixed $value
131
     * @return mixed
132
     * @author Ronan Chilvers <[email protected]>
133
     */
134
    public function setAttribute($attribute, $value)
135
    {
136
        $attribute = Str::snake($attribute);
137
138
        // Handle mutators here
139
        if ($this->hasSetMutator($attribute)) {
140
            $value = $this->setAttributeMutated(
141
                $attribute,
142
                $value
143
            );
144
        }
145
146
        // Handle types here
147
        if ($this->hasType($attribute)) {
148
            $value = $this->getAttributeToRaw(
149
                $attribute,
150
                $value
151
            );
152
        }
153
154
        $attributePrefixed = static::prefix($attribute);
155
        // Are we undoing a previous change?
156
        if (isset($this->oldData[$attributePrefixed]) &&
157
            $value === $this->oldData[$attributePrefixed]) {
158
            unset($this->oldData[$attributePrefixed]);
159
160
        // Keep a record of the old data
161
        } else {
162
            $oldValue = isset($this->data[$attributePrefixed]) ? $this->data[$attributePrefixed] : null;
163
            $this->oldData[$attributePrefixed] = $oldValue;
164
        }
165
        $this->data[$attributePrefixed] = $value;
166
167
        return $this;
168
    }
169
170
    /**
171
     * Is the model or a given field dirty?
172
     *
173
     * @return boolean
174
     * @author Ronan Chilvers <[email protected]>
175
     */
176
    public function isDirty($attribute = null)
177
    {
178
        if (is_null($attribute)) {
179
            return !empty($this->oldData);
180
        }
181
        $attribute = static::prefix($attribute);
182
183
        return array_key_exists($attribute, $this->oldData);
184
    }
185
186
    /**
187
     * Get additional data stored on the model that doesn't relate directly to a column
188
     *
189
     * This is to allow for cases where you load calculated data with a query and want to
190
     * pull it out of the model later.
191
     *
192
     * @param string $key
193
     * @return mixed
194
     * @author Ronan Chilvers <[email protected]>
195
     */
196
    public function getAdditional($key)
197
    {
198
        if (isset($this->data[$key])) {
199
            return $this->data[$key];
200
        }
201
202
        return null;
203
    }
204
205
    /* Attribute getters / setters ******/
206
    /************************************/
207
208
    /************************************/
209
    /* Type handling ********************/
210
211
    /**
212
     * Register a type handler class
213
     *
214
     * @param string $type
215
     * @param \Ronanchilvers\Orm\Features\Type\HandlerInterface $handler
216
     * @return static
217
     * @author Ronan Chilvers <[email protected]>
218
     */
219
    static public function registerTypeHandler(string $type, HandlerInterface $handler)
220
    {
221
        self::$typeHandlers[$type] = $handler;
222
    }
223
224
    /**
225
     * Get a type handler object
226
     *
227
     * @return \Ronanchilvers\Orm\Features\Type\HandlerInterface
228
     * @author Ronan Chilvers <[email protected]>
229
     */
230
    protected function getTypeHandler($type)
231
    {
232
        if (!isset(self::$typeHandlers[$type])) {
233
            return null;
234
        }
235
        $class = self::$typeHandlers[$type];
236
237
        return new $class;
238
    }
239
240
    /**
241
     * Add a type for a given attribute
242
     *
243
     * @param string $type
244
     * @param string $attribute
245
     * @author Ronan Chilvers <[email protected]>
246
     */
247 2
    public function addType($type, $attribute, array $options = [])
248
    {
249 2
        $attribute = Str::snake($attribute);
250 2
        $this->types[$attribute] = [
251 2
            'type'    => $type,
252 2
            'options' => $options,
253 2
        ];
254
    }
255
256
    /**
257
     * Does a given attribute have a specified type?
258
     *
259
     * @param string $attribute
260
     * @return boolean
261
     * @author Ronan Chilvers <[email protected]>
262
     */
263 1
    protected function hasType($attribute)
264
    {
265 1
        $attribute = Str::snake($attribute);
266 1
        if (!isset($this->types[$attribute])) {
267 1
            return false;
268
        }
269
        $type = $this->types[$attribute]['type'];
270
        if (!isset(self::$typeHandlers[$type])) {
271
            return false;
272
        }
273
274
        return true;
275
    }
276
277
    /**
278
     * Get the typed value for a given attribute
279
     *
280
     * @param string $attribute
281
     * @return mixed
282
     * @author Ronan Chilvers <[email protected]>
283
     */
284
    protected function getAttributeToType(
285
        $attribute,
286
        $value
287
    ) {
288
        if (is_null($value)) {
289
            return $value;
290
        }
291
        $handler = $this->getTypeHandler($this->types[$attribute]['type']);
292
293
        return $handler->toType(
294
            $value,
295
            $this->types[$attribute]['options']
296
        );
297
    }
298
299
    /**
300
     * Set the typed value for a given attribute
301
     *
302
     * @param string $attribute
303
     * @param mixed $value
304
     * @return mixed
305
     * @author Ronan Chilvers <[email protected]>
306
     */
307
    protected function getAttributeToRaw(
308
        $attribute,
309
        $value
310
    ) {
311
        if (is_null($value)) {
312
            return $value;
313
        }
314
        $handler = $this->getTypeHandler($this->types[$attribute]['type']);
315
316
        return $handler->toRaw(
317
            $value,
318
            $this->types[$attribute]['options']
319
        );
320
    }
321
322
    /* Type handling ********************/
323
    /************************************/
324
325
    /************************************/
326
    /* Mutator handling *****************/
327
328
    /**
329
     * Does a given attribute have a getter mutator?
330
     *
331
     * @param string $attribute
332
     * @return boolean
333
     * @author Ronan Chilvers <[email protected]>
334
     */
335 1
    protected function hasGetMutator($attribute)
336
    {
337 1
        return method_exists(
338 1
            $this,
339 1
            'get' . Str::pascal($attribute) . 'Attribute'
340 1
        );
341
    }
342
343
    /**
344
     * Does a given attribute have a setter mutator?
345
     *
346
     * @param string $attribute
347
     * @return boolean
348
     * @author Ronan Chilvers <[email protected]>
349
     */
350
    protected function hasSetMutator($attribute)
351
    {
352
        return method_exists(
353
            $this,
354
            'set' . Str::pascal($attribute) . 'Attribute'
355
        );
356
    }
357
358
    /**
359
     * Get the mutated value for an attribute
360
     *
361
     * @param string $attribute
362
     * @param mixed $value
363
     * @return mixed
364
     * @author Ronan Chilvers <[email protected]>
365
     */
366
    protected function getAttributeMutated($attribute, $value)
367
    {
368
        return $this->{'get' . Str::pascal($attribute) . 'Attribute'}(
369
            $value
370
        );
371
    }
372
373
    /**
374
     * Set the mutated value for an attribute
375
     *
376
     * @param string $attribute
377
     * @param mixed $value
378
     * @return mixed
379
     * @author Ronan Chilvers <[email protected]>
380
     */
381
    protected function setAttributeMutated($attribute, $value)
382
    {
383
        return $this->{'get' . Str::pascal($attribute) . 'Attribute'}(
384
            $value
385
        );
386
    }
387
388
    /* Mutator handling *****************/
389
    /************************************/
390
391
    /************************************/
392
    /* Import / export methods **********/
393
394
    /**
395
     * Set the model data from an array
396
     *
397
     * @param array
398
     * @author Ronan Chilvers <[email protected]>
399
     */
400
    public function fromArray(array $data)
401
    {
402
        foreach ($data as $attribute => $value) {
403
            try {
404
                $this->setAttribute($attribute, $value);
405
            } catch (RuntimeException $ex) {
406
                continue;
407
            }
408
        }
409
    }
410
411
    /**
412
     * Get the model as an array
413
     *
414
     * @return array
415
     * @author Ronan Chilvers <[email protected]>
416
     */
417
    public function toArray($unprefixKeys = true)
418
    {
419
        if (true == $unprefixKeys) {
420
            $data = [];
421
            foreach ($this->data as $key => $value) {
422
                $data[static::unprefix($key)] = $value;
423
            }
424
            return $data;
425
        }
426
        return $this->data;
427
    }
428
429
    /* Import / export methods **********/
430
    /************************************/
431
}
432