Passed
Push — master ( 062750...481b43 )
by Marwan
02:32
created

Element::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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