Passed
Push — master ( 48a0b5...08c47d )
by Ronan
04:02
created

HasAttributes::toArray()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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