Passed
Push — master ( 766bf6...808da0 )
by Marwan
07:32
created

Element   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 222
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 37
c 2
b 0
f 0
dl 0
loc 222
ccs 50
cts 50
cp 1
rs 10
wmc 23

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __isset() 0 5 1
A __set() 0 5 1
A offsetSet() 0 3 1
A __unset() 0 5 1
A getIterator() 0 3 1
A offsetGet() 0 3 1
A get() 0 5 1
A offsetExists() 0 3 1
A offsetUnset() 0 3 1
A getAttributes() 0 3 1
A setAttributes() 0 7 3
A __clone() 0 3 1
A __sleep() 0 3 1
A set() 0 7 1
A __get() 0 5 1
A __construct() 0 9 2
A bootstrap() 0 2 1
A assertAttributeExists() 0 12 3
1
<?php
2
3
/**
4
 * @author Marwan Al-Soltany <[email protected]>
5
 * @copyright Marwan Al-Soltany 2021
6
 * For the full copyright and license information, please view
7
 * the LICENSE file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace MAKS\Velox\Backend\Model;
13
14
use MAKS\Velox\Backend\Exception;
15
use MAKS\Velox\Backend\Model\DBAL;
16
use MAKS\Velox\Helper\Misc;
17
18
/**
19
 * An abstract class that holds the base functionality of a model.
20
 * NOTE: This class is not meant to be used directly.
21
 *
22
 * @package Velox\Backend\Model
23
 * @since 1.5.1
24
 */
25
abstract class Element extends DBAL implements \ArrayAccess, \Traversable, \IteratorAggregate
26
{
27
    /**
28
     * Model attributes. Corresponds to table columns.
29
     */
30
    protected array $attributes;
31
32
33
    /**
34
     * Class constructor.
35
     *
36
     * Keep all constructor arguments optional when extending the class.
37
     * Or use `self::bootstrap()` instead.
38
     *
39
     * @param array $attributes [optional] The attributes to set on the model.
40
     */
41 41
    public function __construct(?array $attributes = [])
42
    {
43 41
        $this->attributes = array_merge(array_fill_keys($this->getColumns(), null), $attributes ?? []);
44
45 41
        if ($this->isMigrated() === false) {
46 40
            $this->migrate();
47
        }
48
49 41
        $this->bootstrap();
50
    }
51
52
    /**
53
     * Override this method to add your own bootstrap code to the modal.
54
     *
55
     * @return void
56
     */
57
    protected function bootstrap(): void
58
    {
59
        // implemented as needed
60
    }
61
62
    /**
63
     * Asserts that the model attribute name is valid.
64
     *
65
     * @param mixed $name The name to validate.
66
     *
67
     * @return void
68
     *
69
     * @throws \OutOfBoundsException If attribute name is not a part of model `$columns`.
70
     */
71 32
    protected static function assertAttributeExists($name): void
72
    {
73
        static $columns = null;
74
75 32
        if ($columns === null) {
76 2
            $columns = static::getColumns();
77
        }
78
79 32
        if (!in_array((string)$name, $columns)) {
80 1
            Exception::throw(
81
                'UnknownAttributeException:OutOfBoundsException',
82 1
                sprintf('Cannot find attribute with the name "%s". %s model table does not consist of this column', $name, static::class)
83
            );
84
        }
85
    }
86
87
    /**
88
     * Gets the specified model attribute.
89
     *
90
     * @param string $name Attribute name as specified in `$columns`.
91
     *
92
     * @return mixed Attribute value.
93
     *
94
     * @throws \OutOfBoundsException If the attribute does not exists.
95
     */
96 31
    public function get(string $name)
97
    {
98 31
        $this->assertAttributeExists($name);
99
100 31
        return $this->attributes[$name];
101
    }
102
103
    /**
104
     * Sets the specified model attribute.
105
     *
106
     * @param string $name Attribute name as specified in `$columns`.
107
     * @param mixed $value Attribute value.
108
     *
109
     * @return $this
110
     *
111
     * @throws \OutOfBoundsException If the attribute does not exists.
112
     */
113 32
    public function set(string $name, $value): self
114
    {
115 32
        $this->assertAttributeExists($name);
116
117 32
        $this->attributes[$name] = $value;
118
119 32
        return $this;
120
    }
121
122
    /**
123
     * Gets all model attributes.
124
     *
125
     * @return array Model attributes.
126
     */
127 30
    public function getAttributes(): array
128
    {
129 30
        return $this->attributes;
130
    }
131
132
    /**
133
     * Sets all or a subset of model attributes.
134
     *
135
     * @param array $attributes Model attributes.
136
     *
137
     * @return $this
138
     */
139 32
    public function setAttributes(array $attributes): self
140
    {
141 32
        foreach ($attributes as $key => $value) {
142 32
            in_array($key, $this->getColumns()) && $this->set($key, $value);
143
        }
144
145 32
        return $this;
146
    }
147
148
149
    /**
150
     * Makes attributes accessible via public property access notation.
151
     * Examples: `model_id` as `$model->modelId`
152
     */
153 9
    public function __get(string $name)
154
    {
155 9
        $name = Misc::transform($name, 'snake');
156
157 9
        return $this->get($name);
158
    }
159
160
    /**
161
     * Makes attributes accessible via public property assignment notation.
162
     * Examples: `model_id` as `$model->modelId`
163
     */
164 27
    public function __set(string $name, $value)
165
    {
166 27
        $name = Misc::transform($name, 'snake');
167
168 27
        return $this->set($name, $value);
169
    }
170
171
    /**
172
     * Makes attributes consumable via `isset()`.
173
     */
174 1
    public function __isset(string $name)
175
    {
176 1
        $name = Misc::transform($name, 'snake');
177
178 1
        return $this->get($name) !== null;
179
    }
180
181
    /**
182
     * Makes attributes consumable via `unset()`.
183
     */
184 1
    public function __unset(string $name)
185
    {
186 1
        $name = Misc::transform($name, 'snake');
187
188 1
        return $this->set($name, null);
189
    }
190
191
    /**
192
     * Makes the model safely cloneable via the `clone` keyword.
193
     */
194 1
    public function __clone()
195
    {
196 1
        $this->set($this->getPrimaryKey(), null);
197
    }
198
199
    /**
200
     * Makes the model safely consumable via `serialize()`.
201
     */
202 1
    public function __sleep()
203
    {
204 1
        return ['attributes'];
205
    }
206
207
208
    /**
209
     * `ArrayAccess::offsetGet()` interface implementation.
210
     */
211 1
    public function offsetGet($offset)
212
    {
213 1
        return $this->get($offset);
214
    }
215
216
    /**
217
     * `ArrayAccess::offsetSet()` interface implementation.
218
     */
219 1
    public function offsetSet($offset, $value): void
220
    {
221 1
        $this->set($offset, $value);
222
    }
223
224
    /**
225
     * `ArrayAccess::offsetExists()` interface implementation.
226
     */
227 1
    public function offsetExists($offset): bool
228
    {
229 1
        return $this->get($offset) !== null;
230
    }
231
232
    /**
233
     * `ArrayAccess::offsetUnset()` interface implementation.
234
     */
235 1
    public function offsetUnset($offset): void
236
    {
237 1
        $this->set($offset, null);
238
    }
239
240
241
    /**
242
     * `IteratorAggregate::getIterator()` interface implementation.
243
     */
244 1
    public function getIterator(): \Traversable
245
    {
246 1
        return new \ArrayIterator($this->attributes);
247
    }
248
}
249