Lines 76.56% 196 / 256
Methods 61.25% 49 / 80
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 getDefaults 100.00% 8 / 8 100.00% 1 / 1 1
 getSource 100.00% 1 / 1 100.00% 1 / 1 1
 getShortName 0.00% 0 / 1 0.00% 0 / 1 2
 getEmailFrom 0.00% 0 / 1 0.00% 0 / 1 2
 getEmailRequired 0.00% 0 / 1 0.00% 0 / 1 2
 getTelephoneActivated 0.00% 0 / 1 0.00% 0 / 1 2
 getTelephoneRequired 0.00% 0 / 1 0.00% 0 / 1 2
 getCustomTextfieldActivated 0.00% 0 / 1 0.00% 0 / 1 2
 getCustomTextfieldRequired 0.00% 0 / 1 0.00% 0 / 1 2
 getCustomTextfieldLabel 0.00% 0 / 1 0.00% 0 / 1 2
 getCustomTextfield2Activated 0.00% 0 / 1 0.00% 0 / 1 2
 getCustomTextfield2Required 0.00% 0 / 1 0.00% 0 / 1 2
 getCustomTextfield2Label 0.00% 0 / 1 0.00% 0 / 1 2
 getCaptchaActivatedRequired 0.00% 0 / 1 0.00% 0 / 1 2
 getInfoForAppointment 0.00% 0 / 1 0.00% 0 / 1 2
 getInfoForAllAppointments 0.00% 0 / 1 0.00% 0 / 1 2
 getProvider 72.72% 8 / 11 0.00% 0 / 1 5.51
 getProviderId 100.00% 1 / 1 100.00% 1 / 1 1
 getDayoffList 33.33% 3 / 9 0.00% 0 / 1 12.41
 getClosureList 0.00% 0 / 9 0.00% 0 / 1 30
 getRequestList 100.00% 1 / 1 100.00% 1 / 1 1
 getPreference 100.00% 4 / 4 100.00% 1 / 1 6
 getStatus 100.00% 1 / 1 100.00% 1 / 1 1
 getContactEmail 100.00% 1 / 1 100.00% 1 / 1 1
 getName 100.00% 1 / 1 100.00% 1 / 1 1
 getScopeInfo 100.00% 1 / 1 100.00% 1 / 1 1
 getScopeHint 100.00% 1 / 1 100.00% 1 / 1 1
 getDisplayNumberPrefix 0.00% 0 / 1 0.00% 0 / 1 2
 getAlternateRedirectUrl 0.00% 0 / 2 0.00% 0 / 1 6
 getAppointmentsPerMail 0.00% 0 / 2 0.00% 0 / 1 6
 getSlotsPerAppointment 0.00% 0 / 2 0.00% 0 / 1 6
 getWhitelistedMails 0.00% 0 / 2 0.00% 0 / 1 6
 getReservationDuration 0.00% 0 / 1 0.00% 0 / 1 2
 getWaitingTimeFromQueueList 100.00% 5 / 5 100.00% 1 / 1 1
 getCalculatedWorkstationCount 100.00% 6 / 6 100.00% 1 / 1 3
 getBookableStartDate 100.00% 3 / 3 100.00% 1 / 1 2
 getBookableEndDate 100.00% 3 / 3 100.00% 1 / 1 2
 updateStatusQueue 100.00% 13 / 13 100.00% 1 / 1 4
 incrementDisplayNumber 0.00% 0 / 5 0.00% 0 / 1 6
 hasEmailFrom 100.00% 2 / 2 100.00% 1 / 1 2
 isEmailRequired 100.00% 3 / 3 100.00% 1 / 1 3
 isTelephoneRequired 100.00% 2 / 2 100.00% 1 / 1 2
 hasAccess 100.00% 1 / 1 100.00% 1 / 1 2
 withProviderSourceFrom 100.00% 4 / 4 100.00% 1 / 1 1
 withLessData 100.00% 8 / 8 100.00% 1 / 1 5
 setStatusQueue 100.00% 2 / 2 100.00% 1 / 1 1
 setStatusAvailability 100.00% 2 / 2 100.00% 1 / 1 1
 isNewerThan 100.00% 1 / 1 100.00% 1 / 1 1
 __toString 100.00% 5 / 5 100.00% 1 / 1 1
 [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] __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
11class Scope extends Schema\Entity implements Useraccount\AccessInterface
12{
13    public const string PRIMARY = 'id';
14
15    public static $schema = "scope.json";
16
17    /**
18     * @return (Contact|DayoffList|Provider|int|string)[]
19     *
20     */
21    #[\Override]
22    public function getDefaults()
23    {
24        return [
25            'id' => 0,
26            'source' => 'dldb',
27            'contact' => new Contact(),
28            'provider' => new Provider(),
29            'shortName' => '',
30            'dayoff' => new DayoffList()
31        ];
32    }
33
34    public function getSource(): mixed
35    {
36        return $this->toProperty()->source->get();
37    }
38
39    public function getShortName(): mixed
40    {
41        return $this->toProperty()->shortName->get();
42    }
43
44    public function getEmailFrom(): mixed
45    {
46        return $this->getPreference('client', 'emailFrom', null);
47    }
48
49    public function getEmailRequired(): mixed
50    {
51        return $this->getPreference('client', 'emailRequired', null);
52    }
53
54    public function getTelephoneActivated(): mixed
55    {
56        return $this->getPreference('client', 'telephoneActivated', null);
57    }
58
59    public function getTelephoneRequired(): mixed
60    {
61        return $this->getPreference('client', 'telephoneRequired', null);
62    }
63
64    public function getCustomTextfieldActivated(): mixed
65    {
66        return $this->getPreference('client', 'customTextfieldActivated', null);
67    }
68
69    public function getCustomTextfieldRequired(): mixed
70    {
71        return $this->getPreference('client', 'customTextfieldRequired', null);
72    }
73
74    public function getCustomTextfieldLabel(): mixed
75    {
76        return $this->getPreference('client', 'customTextfieldLabel', '');
77    }
78
79    public function getCustomTextfield2Activated(): mixed
80    {
81        return $this->getPreference('client', 'customTextfield2Activated', null);
82    }
83
84    public function getCustomTextfield2Required(): mixed
85    {
86        return $this->getPreference('client', 'customTextfield2Required', null);
87    }
88
89    public function getCustomTextfield2Label(): mixed
90    {
91        return $this->getPreference('client', 'customTextfield2Label', '');
92    }
93
94    public function getCaptchaActivatedRequired(): mixed
95    {
96        return $this->getPreference('client', 'captchaActivatedRequired', null);
97    }
98
99    public function getInfoForAppointment(): mixed
100    {
101        return $this->getPreference('appointment', 'infoForAppointment', null);
102    }
103
104    public function getInfoForAllAppointments(): mixed
105    {
106        return $this->getPreference('appointment', 'infoForAllAppointments', null);
107    }
108
109
110    public function getProvider(): Provider
111    {
112        if (!isset($this->provider)) {
113            throw new Exception\ScopeMissingProvider("Provider is missing", 500);
114        }
115        $provider = $this->provider;
116        if (!$provider instanceof Provider) {
117            $provider = new Provider($this->toProperty()->provider->get());
118            $this->provider = $provider;
119        }
120        if (!isset($provider['id']) || !$provider->id) {
121            $exception = new Exception\ScopeMissingProvider("No reference to a provider found for scope $this->id");
122            $exception->data['scope'] = $this->getArrayCopy();
123            throw $exception;
124        }
125        return $provider;
126    }
127
128    public function getProviderId(): mixed
129    {
130        return $this->getProvider()->id;
131    }
132
133    public function getDayoffList(): DayoffList
134    {
135        $dayoff = $this->dayoff ?? [];
136        if (!$dayoff instanceof Collection\DayoffList) {
137            $dayoff = is_array($dayoff) ? $dayoff : [];
138            $dayoff = new Collection\DayoffList($dayoff);
139            foreach ($dayoff as $key => $entry) {
140                if (!$entry instanceof Dayoff) {
141                    $dayoff[$key] = new Dayoff($entry);
142                }
143            }
144            $this->dayoff = $dayoff;
145        }
146        return $dayoff;
147    }
148
149    public function getClosureList(): ClosureList
150    {
151        $closure = $this->closure ?? [];
152        if (!$closure instanceof Collection\ClosureList) {
153            $closure = is_array($closure) ? $closure : [];
154            $closure = new Collection\ClosureList($closure);
155            foreach ($closure as $key => $entry) {
156                if (!$entry instanceof Closure) {
157                    $closure[$key] = new Closure($entry);
158                }
159            }
160            $this->closure = $closure;
161        }
162        return $closure;
163    }
164
165    public function getRequestList(): \BO\Zmsentities\Collection\RequestList
166    {
167        return $this->getProvider()->getRequestList();
168    }
169
170    /**
171     * @param false|null|string $isBool
172     *
173     */
174    public function getPreference(string $preferenceKey, string $index, string|false|null $isBool = false, mixed $default = null): mixed
175    {
176        $preference = $this->toProperty()->preferences->$preferenceKey->$index->get($default);
177        return ($isBool !== false && $isBool !== null && $isBool !== '' && $isBool !== '0')
178            ? ($preference ? 1 : 0)
179            : $preference;
180    }
181
182    public function getStatus(string $statusKey, string $index): mixed
183    {
184        return $this->toProperty()->status->$statusKey->$index->get();
185    }
186
187    public function getContactEmail(): mixed
188    {
189        return $this->toProperty()->contact->email->get();
190    }
191
192    public function getName(): mixed
193    {
194        return $this->toProperty()->contact->name->get();
195    }
196
197    public function getScopeInfo(): mixed
198    {
199        return $this->toProperty()->preferences->ticketprinter->buttonName->get();
200    }
201
202    public function getScopeHint(): mixed
203    {
204        return $this->toProperty()->hint->get();
205    }
206
207    public function getDisplayNumberPrefix(): mixed
208    {
209        return $this->toProperty()->preferences->queue->displayNumberPrefix->get();
210    }
211
212    public function getAlternateRedirectUrl(): mixed
213    {
214        $alternateUrl = $this->toProperty()->preferences->client->alternateAppointmentUrl->get();
215
216        return ($alternateUrl) ? $alternateUrl : null;
217    }
218
219    public function getAppointmentsPerMail(): mixed
220    {
221        $appointmentsPerMail = $this->toProperty()->preferences->client->appointmentsPerMail->get();
222
223        return ($appointmentsPerMail) ? $appointmentsPerMail : null;
224    }
225
226    public function getSlotsPerAppointment(): mixed
227    {
228        $slotsPerAppointment = $this->toProperty()->preferences->client->slotsPerAppointment->get();
229
230        return ($slotsPerAppointment) ? $slotsPerAppointment : null;
231    }
232
233    public function getWhitelistedMails(): mixed
234    {
235        $emails = $this->toProperty()->preferences->client->whitelistedMails->get();
236
237        return ($emails) ? $emails : '';
238    }
239
240    public function getReservationDuration(): mixed
241    {
242        return $this->toProperty()->preferences->appointment->reservationDuration->get();
243    }
244
245    public function getWaitingTimeFromQueueList(Collection\QueueList $queueList, \DateTimeInterface $dateTime): mixed
246    {
247        return $queueList->getEstimatedWaitingTime(
248            $this->getPreference('queue', 'processingTimeAverage'),
249            $this->getCalculatedWorkstationCount(),
250            $dateTime
251        );
252    }
253
254    public function getCalculatedWorkstationCount(): mixed
255    {
256        $workstationCount = null;
257        if ($this->getStatus('queue', 'workstationCount') > 0) {
258            $workstationCount = $this->getStatus('queue', 'workstationCount');
259        } elseif ($this->getStatus('queue', 'ghostWorkstationCount') > 0) {
260            $workstationCount = $this->getStatus('queue', 'ghostWorkstationCount');
261        }
262        return $workstationCount;
263    }
264
265    /**
266    * Get last bookable start date of a scope
267    *
268    * @return \DateTimeImmutable $scopeEndDate
269    */
270    public function getBookableStartDate(\DateTimeInterface $now)
271    {
272        $now = Helper\DateTime::create($now);
273        $scopeStartDate = $this->toProperty()->preferences->appointment->startInDaysDefault->get();
274        return ($scopeStartDate) ? $now->modify('+' . $scopeStartDate . 'days') : $now;
275    }
276
277    /**
278    * Get last bookable end date of a scope
279    *
280    * @return \DateTimeImmutable $scopeEndDate
281    */
282    public function getBookableEndDate(\DateTimeInterface $now)
283    {
284        $now = Helper\DateTime::create($now);
285        $scopeEndDate = $this->toProperty()->preferences->appointment->endInDaysDefault->get();
286        return ($scopeEndDate) ? $now->modify('+' . $scopeEndDate . 'days') : $now;
287    }
288
289    public function updateStatusQueue(\DateTimeInterface $dateTime): static
290    {
291        $lastQueueUpdateDate = Helper\DateTime::create()
292            ->setTimestamp($this->getStatus('queue', 'lastGivenNumberTimestamp'));
293        if ($lastQueueUpdateDate->format('Y-m-d') == $dateTime->format('Y-m-d')) {
294            $this->setStatusQueue('lastGivenNumber', $this->getStatus('queue', 'lastGivenNumber') + 1);
295            $this->setStatusQueue('givenNumberCount', $this->getStatus('queue', 'givenNumberCount') + 1);
296        } else {
297            $this->setStatusQueue('lastGivenNumber', $this->getPreference('queue', 'firstNumber'));
298            $this->setStatusQueue('givenNumberCount', 1);
299        }
300        if ($this->getStatus('queue', 'lastGivenNumber') < $this->getPreference('queue', 'firstNumber')) {
301            $this->setStatusQueue('lastGivenNumber', $this->getPreference('queue', 'firstNumber'));
302        } elseif ($this->getStatus('queue', 'lastGivenNumber') > $this->getPreference('queue', 'lastNumber')) {
303            $this->setStatusQueue('lastGivenNumber', $this->getPreference('queue', 'firstNumber'));
304        }
305        $this->setStatusQueue('lastGivenNumberTimestamp', $dateTime->getTimestamp());
306        return $this;
307    }
308
309    public function incrementDisplayNumber(): static
310    {
311        if ($this->getStatus('queue', 'lastDisplayNumber') >= $this->getStatus('queue', 'maxDisplayNumber')) {
312            $this->setStatusQueue('lastDisplayNumber', 1);
313            return $this;
314        }
315
316        $this->setStatusQueue('lastDisplayNumber', $this->getStatus('queue', 'lastDisplayNumber') + 1);
317        return $this;
318    }
319
320    public function hasEmailFrom(): bool
321    {
322        $emailFrom = $this->getPreference('client', 'emailFrom');
323        return ($emailFrom) ? true : false;
324    }
325
326    public function isEmailRequired(): bool
327    {
328        $emailFrom = $this->getPreference('client', 'emailFrom');
329        $emailRequired = $this->getPreference('client', 'emailRequired');
330        return ($emailFrom && $emailRequired) ? true : false;
331    }
332
333    public function isTelephoneRequired(): bool
334    {
335        $telephoneRequired = $this->getPreference('client', 'telephoneRequired');
336        return $telephoneRequired ? true : false;
337    }
338
339    /**
340     * @return bool
341     */
342    #[\Override]
343    public function hasAccess(Useraccount $useraccount): bool
344    {
345        return $useraccount->isSuperUser() ||  $useraccount->hasScope($this->id);
346    }
347
348    /**
349     * Keep provider and source unchanged (non-superusers must not reassign scope location data).
350     */
351    public function withProviderSourceFrom(self $reference): self
352    {
353        $entity = clone $this;
354        $entity['source'] = $reference->getSource();
355        $entity->provider = new Provider($reference->getProvider()->getArrayCopy());
356        return $entity;
357    }
358
359    /**
360     * Reduce data of dereferenced entities to a required minimum
361     *
362     * @return static
363     */
364    #[\Override]
365    public function withLessData(array $keepArray = [])
366    {
367        $entity = clone $this;
368        if (! in_array('dayoff', $keepArray) && $entity->toProperty()->dayoff->isAvailable()) {
369            unset($entity['dayoff']);
370        }
371        if (! in_array('status', $keepArray)) {
372            unset($entity['status']);
373        }
374        if (! in_array('preferences', $keepArray)) {
375            unset($entity['preferences']);
376        }
377
378        return $entity;
379    }
380
381    public function setStatusQueue(string $key, int $value): static
382    {
383        $this->status['queue'][$key] = $value;
384        return $this;
385    }
386
387    public function setStatusAvailability(mixed $key, mixed $value): static
388    {
389        $this->status['availability'][$key] = $value;
390        return $this;
391    }
392
393    /**
394     * Check if scope is newer than given time
395     *
396     * @return bool
397     */
398    public function isNewerThan(\DateTimeInterface $dateTime)
399    {
400        return ($dateTime->getTimestamp() < $this->lastChange);
401    }
402
403    public function __toString()
404    {
405        $string = 'scope#';
406        $string .= $this['id'];
407        $string .= ' ';
408        $string .= $this->getName();
409        return $string;
410    }
411}

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    }
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    }