Completed
Push — master ( c2ffb2...033042 )
by Melech
05:02
created

ModelTrait::__toArray()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 13
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Support\Model\Traits;
15
16
use JsonException;
17
use Valkyrja\Support\Type\Arr;
18
use Valkyrja\Support\Type\Obj;
19
use Valkyrja\Support\Type\Str;
20
21
use function method_exists;
22
use function property_exists;
23
24
/**
25
 * Trait ModelTrait.
26
 *
27
 * @author Melech Mizrachi
28
 */
29
trait ModelTrait
30
{
31
    /**
32
     * The properties to expose.
33
     *
34
     * @var string[]
35
     */
36
    protected static array $exposed = [];
37
38
    /**
39
     * The original properties.
40
     *
41
     * @var array
42
     */
43
    protected static array $originalProperties = [];
44
45
    /**
46
     * Set properties from an array of properties.
47
     *
48
     * @param array $properties
49
     *
50
     * @return static
51
     */
52
    public static function fromArray(array $properties): self
53
    {
54
        $model = new static();
55
56
        $model->__setProperties($properties);
57
58
        return $model;
59
    }
60
61
    /**
62
     * Get a property.
63
     *
64
     * @param string $name The property to get
65
     *
66
     * @return mixed
67
     */
68
    public function __get(string $name)
69
    {
70
        $methodName = 'get' . Str::toStudlyCase($name);
71
72
        if (method_exists($this, $methodName)) {
73
            return $this->$methodName();
74
        }
75
76
        return $this->{$name};
77
    }
78
79
    /**
80
     * Set a property.
81
     *
82
     * @param string $name  The property to set
83
     * @param mixed  $value The value to set
84
     *
85
     * @return void
86
     */
87
    public function __set(string $name, $value): void
88
    {
89
        $methodName = 'set' . Str::toStudlyCase($name);
90
91
        if (method_exists($this, $methodName)) {
92
            $this->$methodName($value);
93
94
            return;
95
        }
96
97
        $this->{$name} = $value;
98
    }
99
100
    /**
101
     * Check if a property is set.
102
     *
103
     * @param string $name The property to check
104
     *
105
     * @return bool
106
     */
107
    public function __isset(string $name): bool
108
    {
109
        $methodName = 'isset' . Str::toStudlyCase($name);
110
111
        if (method_exists($this, $methodName)) {
112
            return $this->$methodName();
113
        }
114
115
        return property_exists($this, $name);
116
    }
117
118
    /**
119
     * Set properties from an array of properties.
120
     *
121
     * @param array $properties
122
     *
123
     * @return void
124
     */
125
    public function __setProperties(array $properties): void
126
    {
127
        // Iterate through the properties
128
        foreach ($properties as $property => $value) {
129
            // Ensure the property exists before blindly setting
130
            if (property_exists($this, $property)) {
131
                if (! isset(static::$originalProperties[$property])) {
132
                    static::$originalProperties[$property] = $value;
133
                }
134
135
                // Set the property
136
                $this->__set($property, $value);
137
            }
138
        }
139
    }
140
141
    /**
142
     * Get a new model with new properties.
143
     *
144
     * @param array $properties The properties to modify
145
     *
146
     * @return $this
147
     */
148
    public function __withProperties(array $properties): self
149
    {
150
        $model = clone $this;
151
152
        $model->__setProperties($properties);
153
154
        return $model;
155
    }
156
157
    /**
158
     * Get model as an array.
159
     *
160
     * @return array
161
     */
162
    public function __toArray(): array
163
    {
164
        // Get the public properties
165
        $properties = Obj::getProperties($this);
166
167
        // Iterate through properties to expose
168
        foreach (static::$exposed as $exposedProperty => $value) {
169
            if (isset($this->{$exposedProperty})) {
170
                $properties[$exposedProperty] = $this->__get($exposedProperty);
171
            }
172
        }
173
174
        return $properties;
175
    }
176
177
    /**
178
     * Get an array of changed properties.
179
     *
180
     * @return array
181
     */
182
    public function __changed(): array
183
    {
184
        $originalProperties = static::$originalProperties;
185
        $changed = [];
186
187
        foreach ($this->__toArray() as $property => $value) {
188
            $originalProperty = $originalProperties[$property] ?? $value;
189
190
            if ($originalProperty !== $value) {
191
                $changed[$property] = $value;
192
            }
193
        }
194
195
        return $changed;
196
    }
197
198
    /**
199
     * Serialize properties for json_encode.
200
     *
201
     * @return array
202
     */
203
    public function jsonSerialize(): array
204
    {
205
        return $this->__toArray();
206
    }
207
208
    /**
209
     * To string.
210
     *
211
     * @throws JsonException
212
     *
213
     * @return string
214
     */
215
    public function __toString(): string
216
    {
217
        return Arr::toString($this->jsonSerialize());
218
    }
219
220
    /**
221
     * Expose hidden fields or all fields.
222
     *
223
     * @param string ...$properties The field(s) to expose
224
     *
225
     * @return void
226
     */
227
    public function __expose(string ...$properties): void
228
    {
229
        foreach ($properties as $property) {
230
            static::$exposed[$property] = true;
231
        }
232
    }
233
234
    /**
235
     * Un-expose hidden fields or all fields.
236
     *
237
     * @param string ...$properties The field(s) to expose
238
     *
239
     * @return void
240
     */
241
    public function __unexpose(string ...$properties): void
242
    {
243
        if (empty($properties)) {
244
            static::$exposed = [];
245
246
            return;
247
        }
248
249
        foreach ($properties as $property) {
250
            unset(static::$exposed[$property]);
251
        }
252
    }
253
}
254