Lines 88.82% 294 / 331
Methods 80.39% 41 / 51
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 getDefaults 100.00% 7 / 7 100.00% 1 / 1 1
 setPeriod 100.00% 4 / 4 100.00% 1 / 1 1
 addDictionaryEntry 100.00% 9 / 9 100.00% 1 / 1 1
 addDataSet 100.00% 7 / 7 100.00% 1 / 1 5
 withLessData 100.00% 8 / 8 100.00% 1 / 1 4
 getPositionByName 100.00% 5 / 5 100.00% 1 / 1 5
 withCalculatedTotals 100.00% 13 / 13 100.00% 1 / 1 8
 withMaxByHour 100.00% 11 / 11 100.00% 1 / 1 8
 withRequestsSum 100.00% 10 / 10 100.00% 1 / 1 6
 getDatesWithRequests 100.00% 17 / 17 100.00% 1 / 1 8
 withAverage 0.00% 0 / 22 0.00% 0 / 1 132
 withUncapturedRequestRowSortedLast 96.00% 24 / 25 0.00% 0 / 1 9
 withMaxAndAverageFromWaitingTime 100.00% 14 / 14 100.00% 1 / 1 10
 withWeightedAverageProcessingTime 95.00% 19 / 20 0.00% 0 / 1 9
 getCalculatedTotals 100.00% 5 / 5 100.00% 1 / 1 4
 toHashed 100.00% 4 / 4 100.00% 1 / 1 1
 getHashData 100.00% 9 / 9 100.00% 1 / 1 8
 toGrouped 100.00% 4 / 4 100.00% 1 / 1 1
 getGroupedHashSet 100.00% 17 / 17 100.00% 1 / 1 8
 [BO\Zmsentities\Schema\Entity] __construct 100.00% 4 / 4 100.00% 1 / 1 2
 [BO\Zmsentities\Schema\Entity] exchangeArray 100.00% 4 / 4 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] offsetSet 100.00% 1 / 1 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] getUnflattenedArray 100.00% 5 / 5 100.00% 1 / 1 2
 [BO\Zmsentities\Schema\Entity] getValidator 100.00% 6 / 6 100.00% 1 / 1 2
 [BO\Zmsentities\Schema\Entity] isValid 100.00% 2 / 2 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] testValid 100.00% 7 / 7 100.00% 1 / 1 2
 [BO\Zmsentities\Schema\Entity] createExample 100.00% 2 / 2 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] getExample 100.00% 5 / 5 100.00% 1 / 1 2
 [BO\Zmsentities\Schema\Entity] readJsonSchema 66.66% 6 / 9 0.00% 0 / 1 3.33
 [BO\Zmsentities\Schema\Entity] getEntityName 100.00% 4 / 4 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] setJsonCompressLevel 100.00% 2 / 2 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] jsonSerialize 100.00% 11 / 11 100.00% 1 / 1 2
 [BO\Zmsentities\Schema\Entity] __toString 100.00% 2 / 2 100.00% 1 / 1 2
 [BO\Zmsentities\Schema\Entity] __get 0.00% 0 / 1 0.00% 0 / 1 2
 [BO\Zmsentities\Schema\Entity] __isset 0.00% 0 / 1 0.00% 0 / 1 2
 [BO\Zmsentities\Schema\Entity] __set 0.00% 0 / 1 0.00% 0 / 1 2
 [BO\Zmsentities\Schema\Entity] __unset 0.00% 0 / 1 0.00% 0 / 1 2
 [BO\Zmsentities\Schema\Entity] __clone 100.00% 3 / 3 100.00% 1 / 1 3
 [BO\Zmsentities\Schema\Entity] addData 100.00% 14 / 14 100.00% 1 / 1 6
 [BO\Zmsentities\Schema\Entity] withData 0.00% 0 / 3 0.00% 0 / 1 2
 [BO\Zmsentities\Schema\Entity] hasId 100.00% 1 / 1 100.00% 1 / 1 2
 [BO\Zmsentities\Schema\Entity] getId 100.00% 2 / 2 100.00% 1 / 1 3
 [BO\Zmsentities\Schema\Entity] toProperty 100.00% 1 / 1 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] hasProperty 100.00% 1 / 1 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] getProperty 100.00% 1 / 1 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] withProperty 0.00% 0 / 3 0.00% 0 / 1 2
 [BO\Zmsentities\Schema\Entity] withCleanedUpFormData 100.00% 6 / 6 100.00% 1 / 1 3
 [BO\Zmsentities\Schema\Entity] getResolveLevel 100.00% 1 / 1 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] setResolveLevel 100.00% 2 / 2 100.00% 1 / 1 1
 [BO\Zmsentities\Schema\Entity] withResolveLevel 100.00% 9 / 9 100.00% 1 / 1 5
 [BO\Zmsentities\Schema\Entity] withReference 100.00% 5 / 5 100.00% 1 / 1 2
15class Exchange extends Schema\Entity
16{
17    public const string PRIMARY = 'firstDay';
18
19    /**
20     * Statistik CASE labels (must match warehouse SQL in ExchangeRequest* queries).
21     */
22    public const string REQUEST_STAT_NAME_UNCATEGORIZED = 'uncategorized';
23
24    public const string REQUEST_STAT_NAME_NONEXISTENT = 'nonexistent';
25
26    public static $schema = "exchange.json";
27
28    /**
29     * @return (Day|array|string)[]
30     *
31     */
32    #[\Override]
33    public function getDefaults()
34    {
35        return [
36            'firstDay' => new Day(),
37            'lastDay' => new Day(),
38            'period' => 'day',
39            'dictionary' => [ ],
40            'data' => [ ]
41        ];
42    }
43
44    public function setPeriod(\DateTimeInterface $firstDay, \DateTimeInterface $lastDay, string $period = 'day'): static
45    {
46        $this->firstDay = (new Day())->setDateTime($firstDay);
47        $this->lastDay = (new Day())->setDateTime($lastDay);
48        $this->period = $period;
49        return $this;
50    }
51
52    public function addDictionaryEntry(mixed $variable, string $type = 'string', string $description = '', string $reference = ''): static
53    {
54        $position = count($this['dictionary']);
55        $this['dictionary'][$position] = [
56            'position' => $position,
57            'variable' => $variable,
58            'type' => $type,
59            'description' => $description,
60            'reference' => $reference
61        ];
62        return $this;
63    }
64
65    /**
66     * @param mixed $values
67     *
68     *
69     * @return void
70     */
71    public function addDataSet($values)
72    {
73        if (!is_array($values) && !$values instanceof \Traversable) {
74            throw new \Exception("Values have to be of type array");
75        }
76        if (!is_array($values)) {
77            $values = iterator_to_array($values, false);
78        }
79        if (count($this->dictionary) != count($values)) {
80            throw new \Exception("Mismatching dictionary settings for values (count mismatch)");
81        }
82        $this->data[] = $values;
83    }
84
85    /**
86     * @return static
87     */
88    #[\Override]
89    public function withLessData()
90    {
91        $entity = clone $this;
92        if (isset($entity['firstDay'])) {
93            unset($entity['firstDay']);
94        }
95        if (isset($entity['lastDay'])) {
96            unset($entity['lastDay']);
97        }
98        if (isset($entity['period'])) {
99            unset($entity['period']);
100        }
101        return $entity;
102    }
103
104    public function getPositionByName(string $name): mixed
105    {
106        if (isset($this->dictionary)) {
107            foreach ($this->dictionary as $entry) {
108                if (isset($entry['variable']) && $entry['variable'] == $name) {
109                    return $entry['position'];
110                }
111            }
112        }
113        return false;
114    }
115
116    public function withCalculatedTotals(array $keysToCalculate = ['count'], string $dateName = 'name'): static
117    {
118        $entity = clone $this;
119        $namePosition = $this->getPositionByName($dateName);
120        if ($namePosition) {
121            $totals = array_fill(0, count($entity->data[0]), 0);
122            $totals[$namePosition] = 'totals';
123            foreach ($keysToCalculate as $name) {
124                $calculatePosition = $this->getPositionByName($name);
125                foreach ($this->data as $item) {
126                    foreach ($item as $position => $data) {
127                        if (is_numeric($data) && $calculatePosition == $position && is_numeric($totals[$position])) {
128                            $totals[$position] += $data;
129                        }
130                    }
131                }
132            }
133            $entity->addDataSet($totals);
134        }
135        return $entity;
136    }
137
138    public function withMaxByHour(array $keysToCalculate = ['count']): static
139    {
140        $entity = clone $this;
141        $maxima = [];
142        foreach ($entity->data as $dateItems) {
143            foreach ($dateItems as $hour => $hourItems) {
144                foreach ($hourItems as $key => $value) {
145                    if (is_numeric($value) && in_array($key, $keysToCalculate)) {
146                        $maxima[$hour][$key] = (
147                          isset($maxima[$hour][$key]) && $maxima[$hour][$key] > $value
148                        ) ? $maxima[$hour][$key] : $value;
149                    }
150                }
151            }
152            $entity->data['max'] = $maxima;
153        }
154        return $entity;
155    }
156
157    public function withRequestsSum(mixed $keysToCalculate = ['requestscount']): static
158    {
159        $entity = clone $this;
160        $sum = [];
161        foreach ($entity->data as $name => $entry) {
162            $sum[$name] = 0;
163            foreach ($entry as $dateItem) {
164                foreach ($dateItem as $key => $value) {
165                    if (is_numeric($value) && in_array($key, $keysToCalculate)) {
166                        $sum[$name] += $value;
167                    }
168                }
169            }
170        }
171        $entity->data['sum'] = $sum;
172        return $entity;
173    }
174
175    /**
176     * Returns all dates containing at least one recorded request.
177     *
178     * @return string[]
179     */
180    public function getDatesWithRequests(): array
181    {
182        $dates = [];
183        $reservedKeys = [
184            'sum',
185            'average_processingtime',
186            'average_processingtime_overall',
187        ];
188
189        foreach ($this->data as $name => $entries) {
190            if (in_array($name, $reservedKeys, true) || !is_iterable($entries)) {
191                continue;
192            }
193
194            foreach ($entries as $date => $entry) {
195                if (
196                    is_array($entry)
197                    && is_numeric($entry['requestscount'] ?? null)
198                    && (int) $entry['requestscount'] > 0
199                ) {
200                    $dates[(string) $date] = true;
201                }
202            }
203        }
204
205        $dates = array_keys($dates);
206        sort($dates);
207
208        return $dates;
209    }
210
211    public function withAverage(mixed $keyToCalculate): static
212    {
213        $entity = clone $this;
214        $average = [];
215
216        foreach ($entity->data as $name => $entry) {
217            if (!is_array($entry) && !($entry instanceof \Traversable)) {
218                // Skip or handle non-iterable $entry appropriately.
219                continue;
220            }
221
222            $sum = 0;
223            $count = 0;
224
225            foreach ($entry as $dateItem) {
226                if (!is_array($dateItem) && !($dateItem instanceof \Traversable)) {
227                    // Skip or handle non-iterable $dateItem appropriately.
228                    continue;
229                }
230
231                foreach ($dateItem as $key => $value) {
232                    if (!is_numeric($value) || $key !== $keyToCalculate) {
233                        // Skip non-numeric values or when the key doesn't match.
234                        continue;
235                    }
236
237                    $sum += $value;
238                    $count++;
239                }
240            }
241
242            $average[$name . '_sum'] = $sum;
243            $average[$name . '_count'] = $count;
244            $average[$name] = $count > 0
245                ? $sum / $count
246                : null;
247        }
248
249        $entity->data['average_' . $keyToCalculate] = $average;
250        return $entity;
251    }
252
253    /**
254     * Sort service rows alphabetically; place synthetic stat rows last
255     * (before aggregated keys sum / average_*).
256     */
257    public function withUncapturedRequestRowSortedLast(): self
258    {
259        $entity = clone $this;
260        if (!is_array($entity->data)) {
261            return $entity;
262        }
263
264        $reserved = ['sum', 'average_processingtime', 'average_processingtime_overall'];
265        $tailStatNames = [
266            self::REQUEST_STAT_NAME_UNCATEGORIZED,
267            self::REQUEST_STAT_NAME_NONEXISTENT,
268        ];
269        $serviceRows = [];
270        foreach ($entity->data as $key => $value) {
271            if (in_array($key, $reserved, true) || in_array($key, $tailStatNames, true)) {
272                continue;
273            }
274            $serviceRows[$key] = $value;
275        }
276
277        uksort($serviceRows, static function ($a, $b) {
278            return strnatcasecmp((string) $a, (string) $b);
279        });
280
281        $ordered = $serviceRows;
282        foreach ($tailStatNames as $tailKey) {
283            if (array_key_exists($tailKey, $entity->data)) {
284                $ordered[$tailKey] = $entity->data[$tailKey];
285            }
286        }
287        foreach ($reserved as $key) {
288            if (array_key_exists($key, $entity->data)) {
289                $ordered[$key] = $entity->data[$key];
290            }
291        }
292        $entity->data = $ordered;
293        return $entity;
294    }
295
296    public function withMaxAndAverageFromWaitingTime(): static
297    {
298        $entity = clone $this;
299        foreach ($entity->data as $date => $dateItems) {
300            $maxima = 0;
301            $total = 0;
302            $count = 0;
303            foreach ($dateItems as $hourItems) {
304                foreach ($hourItems as $key => $value) {
305                    if (is_numeric($value) && 'waitingtime' == $key && 0 < $value) {
306                        $total += $value;
307                        $count += 1;
308                        $maxima = ($maxima > $value) ? $maxima : $value;
309                    }
310                }
311            }
312            $entity->data[$date]['max'] = $maxima;
313            $entity->data[$date]['average'] = (! $total || ! $count) ? 0 : floor($total / $count);
314        }
315        return $entity;
316    }
317
318    public function withWeightedAverageProcessingTime(): static
319    {
320        $entity = clone $this;
321        $weightedSum = 0.0;
322        $totalCount = 0;
323        $excludedNames = [
324            'sum',
325            'average_processingtime',
326            'average_processingtime_overall',
327        ];
328
329        foreach ($entity->data as $name => $dateItems) {
330            if (in_array($name, $excludedNames, true) || !is_iterable($dateItems)) {
331                continue;
332            }
333
334            foreach ($dateItems as $dateItem) {
335                $processingTime = $dateItem['processingtime'] ?? null;
336                $requestCount = $dateItem['requestscount'] ?? null;
337                if (!is_numeric($processingTime) || !is_numeric($requestCount) || $requestCount <= 0) {
338                    continue;
339                }
340
341                $weightedSum += (float) $processingTime * (int) $requestCount;
342                $totalCount += (int) $requestCount;
343            }
344        }
345        $entity->data['average_processingtime_overall'] = $totalCount > 0 ? $weightedSum / $totalCount : null;
346
347        return $entity;
348    }
349
350    public function getCalculatedTotals(): mixed
351    {
352        foreach (array_reverse($this->data) as $item) {
353            foreach ($item as $data) {
354                if ($data == 'totals') {
355                    return $item;
356                }
357            }
358        }
359        return null;
360    }
361
362    public function toHashed(array $hashfields = []): static
363    {
364        $entity = clone $this;
365        $entity->data = $this->getHashData($hashfields);
366        unset($entity->dictionary);
367        return $entity;
368    }
369
370    /**
371     * @return (array|mixed)[]|false
372     *
373     */
374    public function getHashData(array $hashfields = [], bool $first = false): array|false
375    {
376        $hash = [];
377        foreach ($this->dictionary as $entry) {
378            foreach ($this->data as $key => $item) {
379                foreach ($item as $position => $data) {
380                    if ($entry['position'] == $position) {
381                        if (count($hashfields) && in_array($entry['variable'], $hashfields)) {
382                            $hash[$key][$entry['variable']] = $data;
383                        } else {
384                            $hash[$key][$entry['variable']] = $data;
385                        }
386                    }
387                }
388            }
389        }
390        return ($first) ? reset($hash) : $hash;
391    }
392
393    public function toGrouped(array $fields, array $hashfields): static
394    {
395        $entity = clone $this;
396        $entity->data = $this->getGroupedHashSet($fields, $hashfields);
397        unset($entity->dictionary);
398        return $entity;
399    }
400
401    public function getGroupedHashSet(array $fields, array $hashfields): mixed
402    {
403        $list = [];
404        if (count($fields)) {
405            $field = array_shift($fields);
406            $fieldposition = $this->getPositionByName($field);
407
408            $requestscountPosition = $this->getPositionByName('requestscount');
409
410
411            foreach ($this->data as $element) {
412                if (isset($element[$fieldposition])) {
413                    if (!isset($list[$element[$fieldposition]])) {
414                        $list[$element[$fieldposition]] = clone $this;
415                        $list[$element[$fieldposition]]->data = [];
416                    }
417                    if ($requestscountPosition !== false && isset($element[$requestscountPosition])) {
418                        $element[$requestscountPosition] = (int) $element[$requestscountPosition];
419                    }
420
421                    $list[$element[$fieldposition]]->data[] = $element;
422                }
423            }
424
425            foreach ($list as $key => $row) {
426                $list[$key] = $row->getGroupedHashSet($fields, $hashfields);
427            }
428        } else {
429            return $this->getHashData($hashfields, true);
430        }
431        return $list;
432    }
433}

Inherited from BO\Zmsentities\Schema\Entity

59    public function __construct(mixed $input = null, int $flags = \ArrayObject::ARRAY_AS_PROPS, string $iterator_class = "ArrayIterator")
60    {
61        /** @var class-string<\ArrayIterator> $iterator_class */
62        parent::__construct($this->getDefaults(), $flags, $iterator_class);
63        if ($input) {
64            $input = $this->getUnflattenedArray($input);
65            $this->addData($input);
66        }
67    }
70    public function exchangeArray(object|array $input): array
71    {
72        parent::exchangeArray($this->getDefaults());
73        $input = $this->getUnflattenedArray($input);
74        $this->addData($input);
75        return $this->getArrayCopy();
76    }
82    public function offsetSet(mixed $key, mixed $value): void
83    {
84        /** @psalm-suppress PossiblyNullArgument */
85        parent::offsetSet($key, $value);
86    }
88    public function getUnflattenedArray(mixed $input): mixed
89    {
90        if (!$input instanceof UnflattedArray) {
91            $input = new UnflattedArray($input);
92            $input->getUnflattenedArray();
93        }
94        $input = $input->getValue();
95        return $input;
96    }
112    public function getValidator(string $locale = 'de_DE', int $resolveLevel = 0): Validator
113    {
114        $jsonSchema = self::readJsonSchema()->withResolvedReferences($resolveLevel);
115        $data = (new Schema($this))->withoutRefs();
116        if (Property::__keyExists('$schema', $data)) {
117            unset($data['$schema']);
118        }
119        $validator = new Validator($data->toJsonObject(true), $jsonSchema, $locale);
120        return $validator;
121    }
128    public function isValid(int $resolveLevel = 0): bool
129    {
130        $validator = $this->getValidator('de_DE', $resolveLevel = 0);
131        return $validator->isValid();
132    }
140    public function testValid(string $locale = 'de_DE', int $resolveLevel = 0): bool
141    {
142        $validator = $this->getValidator($locale, $resolveLevel);
143        if (!$validator->isValid()) {
144            $exception = new \BO\Zmsentities\Exception\SchemaValidation();
145            $exception->setSchemaName($this->getEntityName());
146            $exception->setValidationError($validator->getErrors());
147            throw $exception;
148        }
149        return true;
150    }
157    public static function createExample()
158    {
159        $object = new static();
160        return $object->getExample();
161    }
168    public static function getExample()
169    {
170        $class = get_called_class();
171        $jsonSchema = self::readJsonSchema();
172        if ($jsonSchema->offsetExists('example')) {
173            return new $class($jsonSchema['example']);
174        }
175        return new $class();
176    }
178    protected static function readJsonSchema(): mixed
179    {
180        $class = get_called_class();
181        if (!array_key_exists($class, self::$schemaCache)) {
182            $schemaFile = $class::$schema;
183            if (!is_string($schemaFile)) {
184                throw new \BO\Zmsentities\Exception\SchemaMissingJsonFile(
185                    'Missing JSON-Schema file for ' . $class
186                );
187            }
188            self::$schemaCache[$class] = Loader::asArray($schemaFile);
189        }
190        return self::$schemaCache[$class];
191    }
193    public function getEntityName(): string
194    {
195        $entity = get_class($this);
196        $entity = preg_replace('#.*[\\\]#', '', $entity) ?? '';
197        $entity = strtolower($entity);
198        return $entity;
199    }
201    public function setJsonCompressLevel(mixed $jsonCompressLevel): static
202    {
203        $this->jsonCompressLevel = $jsonCompressLevel;
204        return $this;
205    }
208    public function jsonSerialize(): mixed
209    {
210        $schema = array(
211            '$schema' => 'https://schema.berlin.de/queuemanagement/' . $this->getEntityName() . '.json'
212        );
213        $schema = array_merge($schema, $this->getArrayCopy());
214        if ($this instanceof \BO\Zmsentities\Helper\NoSanitize) {
215            $serialize = $schema;
216        } else {
217            $schema = new Schema($schema);
218            $schema->setDefaults($this->getDefaults());
219            $schema->setJsonCompressLevel($this->jsonCompressLevel);
220            $serialize = $schema->toJsonObject();
221        }
222        return $serialize;
223    }
225    public function __toString()
226    {
227        $encoded = json_encode($this->jsonSerialize(), JSON_HEX_QUOT);
228        return $encoded !== false ? $encoded : '';
229    }
235    public function __get(string $name): mixed
236    {
237        return $this->offsetGet($name);
238    }
240    public function __isset(string $name): bool
241    {
242        return $this->offsetExists($name);
243    }
245    public function __set(string $name, mixed $value): void
246    {
247        $this->offsetSet($name, $value);
248    }
250    public function __unset(string $name): void
251    {
252        $this->offsetUnset($name);
253    }
255    public function __clone()
256    {
257        foreach ($this as $key => $property) {
258            if (is_object($property)) {
259                $this[$key] = clone $property;
260            }
261        }
262    }
268    public function addData(array|object $mergeData): static
269    {
270        foreach ($mergeData as $key => $item) {
271            if (isset($this[$key])) {
272                if ($this[$key] instanceof Entity) {
273                    $this[$key]->setResolveLevel(($this->getResolveLevel() ?? 0) - 1);
274                    $this[$key]->addData($item);
275                } elseif ($this[$key] instanceof \BO\Zmsentities\Collection\Base) {
276                    $this[$key]->exchangeArray([]);
277                    $this[$key]->setResolveLevel(($this->getResolveLevel() ?? 0) - 1);
278                    $this[$key]->addData($item);
279                } elseif (is_array($this[$key])) {
280                    $this[$key] = array_replace_recursive($this[$key], $item);
281                } else {
282                    $this[$key] = $item;
283                }
284            } else {
285                $this[$key] = $item;
286            }
287        }
288        return $this;
289    }
294    public function withData(mixed $mergeData): static
295    {
296        $entity = clone $this;
297        $entity->addData($mergeData);
298        return $entity;
299    }
301    public function hasId(): bool
302    {
303        return (false !== $this->getId()) ? true : false;
304    }
306    public function getId(): mixed
307    {
308        $idName = $this::PRIMARY;
309        return ($this->offsetExists($idName) && $this[$idName]) ? $this[$idName] : false;
310    }
317    public function toProperty()
318    {
319        return new \BO\Zmsentities\Helper\Property($this);
320    }
322    public function hasProperty(mixed $propertyName): mixed
323    {
324        return $this->toProperty()->{$propertyName}->isAvailable();
325    }
327    public function getProperty(string $propertyName, mixed $default = ''): mixed
328    {
329        return $this->toProperty()->{$propertyName}->get($default);
330    }
335    public function withProperty(mixed $propertyName, mixed $newValue): static
336    {
337        $entity = clone $this;
338        $entity[$propertyName] = $newValue;
339        return $entity;
340    }
357    public function withCleanedUpFormData()
358    {
359        $entity = clone $this;
360        if (isset($entity['save'])) {
361            unset($entity['save']);
362        }
363        if (isset($entity['removeImage'])) {
364            unset($entity['removeImage']);
365        }
366        return $entity;
367    }
372    public function getResolveLevel()
373    {
374        return $this->resolveLevel;
375    }
381    public function setResolveLevel($resolveLevel)
382    {
383        $this->resolveLevel = $resolveLevel;
384        return $this;
385    }
393    public function withResolveLevel($resolveLevel): self|array
394    {
395        if ($resolveLevel >= 0) {
396            $entity = clone $this;
397            foreach ($entity as $key => $value) {
398                if ($value instanceof Entity || $value instanceof \BO\Zmsentities\Collection\Base) {
399                    $entity[$key] = $value->withResolveLevel($resolveLevel - 1);
400                } else {
401                    $entity[$key] = $value;
402                }
403            }
404            $entity->setResolveLevel($resolveLevel);
405            return $entity;
406        } else {
407            return $this->withReference();
408        }
409    }
417    public function withReference($additionalData = []): self|array
418    {
419        if (isset($this[$this::PRIMARY])) {
420            $additionalData['$ref'] =
421                $this::$schemaRefPrefix . $this->getEntityName() . '/' . $this[$this::PRIMARY] . '/';
422            return $additionalData;
423        } else {
424            return $this->withResolveLevel(0);
425        }
426    }