Passed
Push — master ( cbecdb...34e2d9 )
by Arthur
07:30
created

DataTransferObject::disableValidation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Larapie\DataTransferObject;
6
7
use Larapie\DataTransferObject\Contracts\DtoContract;
8
use Larapie\DataTransferObject\Contracts\WithAdditionalProperties;
9
use Larapie\DataTransferObject\Exceptions\ImmutableDtoException;
10
use Larapie\DataTransferObject\Exceptions\ImmutablePropertyDtoException;
11
use Larapie\DataTransferObject\Exceptions\PropertyAlreadyExistsException;
12
use Larapie\DataTransferObject\Exceptions\PropertyNotFoundDtoException;
13
use Larapie\DataTransferObject\Exceptions\ValidatorException;
14
use Larapie\DataTransferObject\Factories\PropertyFactory;
15
use Larapie\DataTransferObject\Property\Property;
16
use ReflectionException;
17
18
/**
19
 * Class DataTransferObject.
20
 */
21
abstract class DataTransferObject implements DtoContract
22
{
23
    /** @var array */
24
    protected $onlyKeys = [];
25
26
    /** @var array */
27
    protected $with = [];
28
29
    /** @var Property[] */
30
    protected $properties = [];
31
32
    /** @var bool */
33
    protected $immutable = false;
34
35
    /** @var bool */
36
    protected $validation = true;
37
38
    public function __construct(array $parameters)
39
    {
40
        $this->boot($parameters);
41
    }
42
43
    /**
44
     * Boot the dto and process all parameters.
45
     * @param array $parameters
46
     * @throws ReflectionException
47
     */
48
    protected function boot(array $parameters): void
49
    {
50
        $this->properties = (new PropertyFactory($this))->build($parameters);
51
    }
52
53
    public function setImmutable(bool $immutable): void
54
    {
55
        if ($immutable) {
56
            if (!$this->isImmutable()) {
57
                $this->immutable = true;
58
                foreach ($this->properties as $property) {
59
                    $property->chainImmutable($immutable);
60
                }
61
            }
62
        } else {
63
            $this->immutable = true;
64
        }
65
    }
66
67
    /**
68
     * Immutable behavior
69
     * Throw error if a user tries to set a property.
70
     * @param $name
71
     * @param $value
72
     * @throws ImmutableDtoException|ImmutablePropertyDtoException|PropertyNotFoundDtoException
73
     */
74
    public function __set($name, $value)
75
    {
76
        if ($this->immutable) {
77
            throw new ImmutableDtoException($name);
78
        }
79
        if (!isset($this->properties[$name])) {
80
            throw new PropertyNotFoundDtoException($name, get_class($this));
81
        }
82
83
        if ($this->properties[$name]->isImmutable()) {
84
            throw new ImmutablePropertyDtoException($name);
85
        }
86
        $this->$name = $value;
87
    }
88
89
    protected function propertyExists(string $propertyName)
90
    {
91
        return array_key_exists($propertyName, $this->properties);
92
    }
93
94
    public function &__get($name)
95
    {
96
        if (!$this->propertyExists($name)) {
97
            if ($this instanceof WithAdditionalProperties) {
98
                if (array_key_exists($name, $this->with)) {
99
                    if ($this->isImmutable()) {
100
                        $value = $this->with[$name];
101
102
                        return $value;
103
                    }
104
105
                    return $this->with[$name];
106
                }
107
                throw new PropertyNotFoundDtoException($name, static::class);
108
            }
109
        }
110
        $property = $this->properties[$name];
111
        $violations = $property->getViolations();
112
        if ($violations->count() > 0) {
113
            throw new ValidatorException([$name => $violations]);
114
        }
115
        if ($this->isImmutable()) {
116
            $value = $this->properties[$name]->getValue();
117
118
            return $value;
119
        }
120
121
        return $this->properties[$name]->value;
122
    }
123
124
    public function isImmutable(): bool
125
    {
126
        return $this->immutable;
127
    }
128
129
    public function isValid()
130
    {
131
        return empty($this->getValidationViolations());
132
    }
133
134
    public function validationEnabled()
135
    {
136
        return $this->validation;
137
    }
138
139
    public function enableValidation()
140
    {
141
        $this->validation = true;
142
    }
143
144
    public function disableValidation()
145
    {
146
        $this->validation = false;
147
    }
148
149
    public function all(): array
150
    {
151
        $data = [];
152
153
        if ($this->validation) {
154
            $this->validate();
155
        }
156
157
        foreach ($this->properties as $property) {
158
            $data[$property->getName()] = ($value = $property->getValue()) instanceof DataTransferObject ? $value->toArray() : $value;
159
        }
160
161
        return array_merge($data, $this->with);
162
    }
163
164
    public function only(string ...$keys): DtoContract
165
    {
166
        $this->onlyKeys = array_merge($this->onlyKeys, $keys);
167
168
        return $this;
169
    }
170
171
    public function except(string ...$keys): DtoContract
172
    {
173
        foreach ($keys as $key) {
174
            if (array_key_exists($key, $this->with)) {
175
                unset($this->with[$key]);
176
            }
177
            $property = $this->properties[$key] ?? null;
178
            if (isset($property)) {
179
                $property->setVisible(false);
180
            }
181
        }
182
183
        return $this;
184
    }
185
186
    public function with(string $key, $value): DtoContract
187
    {
188
        if (array_key_exists($key, $this->properties)) {
189
            throw new PropertyAlreadyExistsException($key);
190
        }
191
192
        return $this->override($key, $value);
193
    }
194
195
    public function override(string $key, $value): DtoContract
196
    {
197
        if ($this->isImmutable()) {
198
            throw new ImmutableDtoException($key);
199
        }
200
        if (($propertyExists = array_key_exists($key, $this->properties) && $this->properties[$key]->isImmutable())) {
201
            throw new ImmutablePropertyDtoException($key);
202
        }
203
204
        if ($propertyExists) {
0 ignored issues
show
introduced by
The condition $propertyExists is always false.
Loading history...
205
            $property = $this->properties[$key];
206
            $property->set($value);
207
            if ($this->validation) {
208
                $this->validate();
209
            }
210
        } else {
211
            $this->with[$key] = $value;
212
        }
213
214
        return $this;
215
    }
216
217
    public function toArray(): array
218
    {
219
        $data = $this->all();
220
        $array = [];
221
222
        if (count($this->onlyKeys)) {
223
            $array = array_intersect_key($data, array_flip((array)$this->onlyKeys));
224
        } else {
225
            foreach ($data as $key => $propertyValue) {
226
                if (array_key_exists($key, $this->with) || (array_key_exists($key, $this->properties) && $this->properties[$key]->isVisible() && $this->properties[$key]->isInitialized())) {
227
                    $array[$key] = $propertyValue;
228
                }
229
            }
230
        }
231
232
        return $this->parseArray($array);
233
    }
234
235
    protected function parseArray(array $array): array
236
    {
237
        foreach ($array as $key => $value) {
238
            if (
239
                $value instanceof DataTransferObject
240
                || $value instanceof DataTransferObjectCollection
241
            ) {
242
                $array[$key] = $value->toArray();
243
244
                continue;
245
            }
246
247
            if (!is_array($value)) {
248
                continue;
249
            }
250
251
            $array[$key] = $this->parseArray($value);
252
        }
253
254
        return $array;
255
    }
256
257
    public function getValidationViolations()
258
    {
259
        $violations = [];
260
        foreach ($this->properties as $name => $property) {
261
            $value = $property->getValue();
262
            if ($value instanceof DataTransferObject) {
263
                $nestedViolations = $this->recursivelySortKeys($value->getValidationViolations(), $name);
264
                $violations = array_merge($violations, $nestedViolations);
265
            }
266
            if (is_iterable($value)) {
267
                foreach ($value as $key => $potentialDto) {
268
                    if ($potentialDto instanceof DataTransferObject) {
269
                        $nestedViolations = $this->recursivelySortKeys($potentialDto->getValidationViolations(), "$name.$key");
270
                        $violations = array_merge($violations, $nestedViolations);
271
                    }
272
                }
273
            }
274
            $violationList = $property->getViolations();
275
            if ($violationList === null || $violationList->count() <= 0) {
276
                continue;
277
            }
278
            $violations[$name] = $violationList;
279
        }
280
281
        return $violations;
282
    }
283
284
    public function validate()
285
    {
286
        $violations = $this->getValidationViolations();
287
        if (!empty($violations)) {
288
            throw new ValidatorException($violations);
289
        }
290
        return true;
291
    }
292
293
    protected function recursivelySortKeys(array $array, $str = '')
294
    {
295
        $sortedArray = [];
296
        foreach ($array as $key => $val) {
297
            if (is_array($val)) {
298
                if ($str == '') {
299
                    $this->recursivelySortKeys($val, $key);
300
                } else {
301
                    $this->recursivelySortKeys($val, $str . '.' . $key);
302
                }
303
            } else {
304
                if ($str == '') {
305
                    $sortedArray[$key] = $val;
306
                    echo $key . "\n";
307
                } else {
308
                    $sortedArray[$str . '.' . $key] = $val;
309
                }
310
            }
311
        }
312
313
        return $sortedArray;
314
    }
315
}
316