Passed
Push — master ( 3e0ae5...66dbef )
by Melech
01:47 queued 20s
created

Entity::internalGetPropertyValueForDataStore()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 28
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 4
nop 2
dl 0
loc 28
rs 9.5555
c 0
b 0
f 0
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\Orm\Entity\Abstract;
15
16
use JsonException;
17
use Override;
0 ignored issues
show
Bug introduced by
The type Override was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Valkyrja\Orm\Entity\Contract\Entity as Contract;
19
use Valkyrja\Orm\Repository\Contract\Repository;
20
use Valkyrja\Throwable\Exception\InvalidArgumentException;
21
use Valkyrja\Throwable\Exception\RuntimeException;
22
use Valkyrja\Type\BuiltIn\Support\Arr;
23
use Valkyrja\Type\Contract\Type;
24
use Valkyrja\Type\Data\Cast;
25
use Valkyrja\Type\Model\Abstract\Model;
26
use Valkyrja\Type\Model\Trait\Castable;
27
use Valkyrja\Type\Model\Trait\ProtectedExposable;
28
29
use function array_walk;
30
use function gettype;
31
use function is_array;
32
use function is_int;
33
use function is_string;
34
35
/**
36
 * Class Entity.
37
 *
38
 * @author Melech Mizrachi
39
 *
40
 * @psalm-import-type StorableValue from Contract
41
 *
42
 * @phpstan-import-type StorableValue from Contract
43
 */
44
abstract class Entity extends Model implements Contract
45
{
46
    use Castable;
0 ignored issues
show
introduced by
The trait Valkyrja\Type\Model\Trait\Castable requires some properties which are not provided by Valkyrja\Orm\Entity\Abstract\Entity: $isArray, $type, $convert
Loading history...
47
    use ProtectedExposable;
48
49
    /**
50
     * The table name.
51
     *
52
     * @var non-empty-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
53
     */
54
    protected static string $tableName;
55
56
    /**
57
     * The id field.
58
     *
59
     * @var non-empty-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
60
     */
61
    protected static string $idField = 'id';
62
63
    /**
64
     * The repository.
65
     *
66
     * @var class-string<Repository>|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<Repository>|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<Repository>|null.
Loading history...
67
     */
68
    protected static string|null $repository = null;
69
70
    /**
71
     * A list of hidden fields we can expose for storage.
72
     *
73
     * @var non-empty-string[]
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string[] at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string[].
Loading history...
74
     */
75
    protected static array $relationshipProperties = [];
76
77
    /**
78
     * A list of fields we do not want to store.
79
     *
80
     * @var non-empty-string[]
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string[] at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string[].
Loading history...
81
     */
82
    protected static array $unStorableFields = [];
83
84
    /**
85
     * @inheritDoc
86
     */
87
    #[Override]
88
    public static function getTableName(): string
89
    {
90
        return static::$tableName;
91
    }
92
93
    /**
94
     * @inheritDoc
95
     */
96
    #[Override]
97
    public static function getIdField(): string
98
    {
99
        return static::$idField;
100
    }
101
102
    /**
103
     * @inheritDoc
104
     */
105
    #[Override]
106
    public static function getRepository(): string|null
107
    {
108
        return static::$repository;
109
    }
110
111
    /**
112
     * @inheritDoc
113
     */
114
    #[Override]
115
    public static function getRelationshipProperties(): array
116
    {
117
        return static::$relationshipProperties;
118
    }
119
120
    /**
121
     * @inheritDoc
122
     */
123
    #[Override]
124
    public static function getUnStorableFields(): array
125
    {
126
        return static::$unStorableFields;
127
    }
128
129
    /**
130
     * Get the id field's value.
131
     */
132
    #[Override]
133
    public function getIdValue(): string|int
134
    {
135
        /** @var mixed $id */
136
        $id = $this->__get(static::getIdField());
137
138
        if (is_int($id) || (is_string($id) && $id !== '')) {
139
            /** @var non-empty-string|int $id */
140
            return $id;
141
        }
142
143
        throw new RuntimeException('Id field value should be a string or int');
144
    }
145
146
    /**
147
     * @inheritDoc
148
     *
149
     * @param string ...$properties [optional] An array of properties to return
150
     *
151
     * @throws JsonException
152
     *
153
     * @return array<non-empty-string, StorableValue>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<non-empty-string, StorableValue> at position 2 could not be parsed: Unknown type name 'non-empty-string' at position 2 in array<non-empty-string, StorableValue>.
Loading history...
154
     */
155
    #[Override]
156
    public function asStorableArray(string ...$properties): array
157
    {
158
        $unStorableFields = array_merge(static::getUnStorableFields(), static::getRelationshipProperties());
159
        // Get the public properties
160
        $allProperties = $this->internalAllPropertiesForStorage();
161
        $castings      = $this->internalGetCastings();
162
163
        $this->internalRemoveInternalProperties($allProperties);
164
165
        /** @var array<string, mixed> $allProperties */
166
        $allProperties = $this->internalCheckOnlyProperties($allProperties, $properties);
167
168
        $this->internalRemoveUnStorableFields($allProperties, $unStorableFields);
169
170
        /** @var array<non-empty-string, StorableValue> $properties */
171
        $properties = $this->internalGetPropertyValuesForDataStore($allProperties, $castings);
172
173
        return $properties;
174
    }
175
176
    /**
177
     * @inheritDoc
178
     *
179
     * @throws JsonException
180
     */
181
    #[Override]
182
    public function asStorableChangedArray(): array
183
    {
184
        /** @var array<non-empty-string, StorableValue> $properties */
185
        $properties = $this->internalGetChangedProperties($this->asStorableArray());
186
187
        return $properties;
188
    }
189
190
    /**
191
     * Get all properties for storage.
192
     *
193
     * @return array<string, mixed>
194
     */
195
    protected function internalAllPropertiesForStorage(): array
196
    {
197
        /** @var array<string, mixed> */
198
        return get_object_vars($this);
199
    }
200
201
    /**
202
     * Remove un-storable entity properties from an array of properties.
203
     *
204
     * @param array<string, mixed> $allProperties    The properties
205
     * @param string[]             $unStorableFields The un-storable fields
206
     *
207
     * @return void
208
     */
209
    protected function internalRemoveUnStorableFields(array &$allProperties, array $unStorableFields): void
210
    {
211
        array_walk($unStorableFields, static function (string $unStorableHiddenField) use (&$allProperties): void {
212
            // Remove the un-storable field to the all properties array
213
            unset($allProperties[$unStorableHiddenField]);
214
        });
215
    }
216
217
    /**
218
     * Get property values for data store.
219
     *
220
     * @param array<string, mixed> $allProperties The properties
221
     * @param array<string, Cast>  $castings      The castings
222
     *
223
     * @throws JsonException
224
     *
225
     * @return array<non-empty-string, StorableValue>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<non-empty-string, StorableValue> at position 2 could not be parsed: Unknown type name 'non-empty-string' at position 2 in array<non-empty-string, StorableValue>.
Loading history...
226
     */
227
    protected function internalGetPropertyValuesForDataStore(array $allProperties, array $castings): array
228
    {
229
        array_walk(
230
            $allProperties,
231
            fn (mixed &$value, string $property): mixed => /** @var mixed $value */ $value
232
                = $this->internalGetPropertyValueForDataStore($castings, $property)
233
        );
234
235
        /** @var array<non-empty-string, StorableValue> $allProperties */
236
        return $allProperties;
237
    }
238
239
    /**
240
     * Get a property's value for data store.
241
     *
242
     * @param array<string, Cast> $castings The castings
243
     * @param string              $property The property name
244
     *
245
     * @throws JsonException
246
     *
247
     * @return mixed
248
     */
249
    protected function internalGetPropertyValueForDataStore(array $castings, string $property): mixed
250
    {
251
        $value = $this->__get($property);
252
253
        // If there is no type specified or the value is null just return the value
254
        // Castings assignment is set in the if specifically to avoid an assignment
255
        // if the value is null, which would be an unneeded assigned variable
256
        if ($value === null || ($cast = $castings[$property] ?? null) === null) {
257
            return $value;
258
        }
259
260
        // An array would indicate an array of types
261
        if ($cast->isArray) {
262
            if (! is_array($value)) {
263
                $typeOf = gettype($value);
264
265
                throw new InvalidArgumentException("Expecting array, $typeOf provided, for $property cast");
266
            }
267
268
            return Arr::toString(
269
                array_map(
270
                    fn (mixed $data): mixed => $this->internalGetTypeValueForDataStore($cast, $data),
271
                    $value
272
                )
273
            );
274
        }
275
276
        return $this->internalGetTypeValueForDataStore($cast, $value);
277
    }
278
279
    /**
280
     * Get a Type type cast value for data store.
281
     *
282
     * @param Cast  $cast  The cast for the property
283
     * @param mixed $value The value
284
     *
285
     * @return mixed
286
     */
287
    protected function internalGetTypeValueForDataStore(Cast $cast, mixed $value): mixed
288
    {
289
        /** @var class-string<Type> $type */
290
        $type = $cast->type;
291
292
        if ($value instanceof Type) {
293
            return $value->asFlatValue();
294
        }
295
296
        $typeInstance = $type::fromValue($value);
297
298
        return $typeInstance->asFlatValue();
299
    }
300
}
301