Lines 87.50% 175 / 200
Methods 85.71% 36 / 42
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 getArrayCopy 100.00% 1 / 1 100.00% 1 / 1 1
 getDay 100.00% 20 / 20 100.00% 1 / 1 8
 getDayByDateTime 100.00% 1 / 1 100.00% 1 / 1 1
 getDayByDay 100.00% 1 / 1 100.00% 1 / 1 1
 hasDay 100.00% 2 / 2 100.00% 1 / 1 2
 withAssociatedDays 100.00% 8 / 8 100.00% 1 / 1 2
 setStatusByType 100.00% 3 / 3 100.00% 1 / 1 2
 withAddedDayList 100.00% 4 / 4 100.00% 1 / 1 3
 setSortByDate 100.00% 7 / 7 100.00% 1 / 1 1
 setSort 100.00% 4 / 4 100.00% 1 / 1 1
 hasDayWithAppointments 100.00% 5 / 5 100.00% 1 / 1 3
 getFirstBookableDay 100.00% 5 / 5 100.00% 1 / 1 3
 withDaysInDateRange 100.00% 8 / 8 100.00% 1 / 1 4
 withDaysFromPeriod 100.00% 7 / 7 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] __construct 100.00% 1 / 1 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] getFirst 100.00% 2 / 2 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] getLast 100.00% 3 / 3 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] sortByName 100.00% 7 / 7 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] sortByContactName 100.00% 7 / 7 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] sortByCustomKey 100.00% 4 / 4 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] sortByCustomStringKey 100.00% 7 / 7 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] __clone 100.00% 2 / 2 100.00% 1 / 1 2
 [BO\Zmsentities\Collection\Base] hasEntity 100.00% 4 / 4 100.00% 1 / 1 4
 [BO\Zmsentities\Collection\Base] getEntity 100.00% 4 / 4 100.00% 1 / 1 4
 [BO\Zmsentities\Collection\Base] addEntity 100.00% 2 / 2 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] offsetSet 100.00% 9 / 9 100.00% 1 / 1 3
 [BO\Zmsentities\Collection\Base] addData 91.66% 11 / 12 0.00% 0 / 1 5.01
 [BO\Zmsentities\Collection\Base] addList 100.00% 3 / 3 100.00% 1 / 1 2
 [BO\Zmsentities\Collection\Base] getIds 100.00% 4 / 4 100.00% 1 / 1 2
 [BO\Zmsentities\Collection\Base] getIdsCsv 100.00% 1 / 1 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] getCsvForProperty 0.00% 0 / 5 0.00% 0 / 1 12
 [BO\Zmsentities\Collection\Base] getCsvForPropertyList 100.00% 11 / 11 100.00% 1 / 1 6
 [BO\Zmsentities\Collection\Base] withValueFor 0.00% 0 / 4 0.00% 0 / 1 6
 [BO\Zmsentities\Collection\Base] withLessData 100.00% 4 / 4 100.00% 1 / 1 2
 [BO\Zmsentities\Collection\Base] setJsonCompressLevel 0.00% 0 / 2 0.00% 0 / 1 6
 [BO\Zmsentities\Collection\Base] getAsArray 0.00% 0 / 4 0.00% 0 / 1 6
 [BO\Zmsentities\Collection\Base] jsonSerialize 100.00% 1 / 1 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] __toString 100.00% 4 / 4 100.00% 1 / 1 2
 [BO\Zmsentities\Collection\Base] getResolveLevel 100.00% 1 / 1 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] setResolveLevel 100.00% 2 / 2 100.00% 1 / 1 1
 [BO\Zmsentities\Collection\Base] withResolveLevel 100.00% 5 / 5 100.00% 1 / 1 2
 [BO\Zmsentities\Collection\Base] chunk 0.00% 0 / 9 0.00% 0 / 1 12
10class DayList extends Base implements JsonUnindexed
11{
12    public const string ENTITY_CLASS = '\BO\Zmsentities\Day';
13
14    #[\Override]
15    public function getArrayCopy(): array
16    {
17        return parent::getArrayCopy();
18    }
19
20    /**
21     * ATTENTION: Performance critical, keep highly optimized
22     *
23     * @return ($createDay is true ? Day : Day|null)
24     */
25    public function getDay(string $year, string $month, string $dayNumber, bool $createDay = true): Day|null
26    {
27        $dateHash = Day::getCalculatedDayHash($dayNumber, $month, $year);
28        if ($this->offsetExists($dateHash)) {
29            return $this[$dateHash];
30        }
31        foreach ($this as $key => $day) {
32            if (!$day instanceof Day) {
33                $day = new Day($day);
34                $this[$key] = $day;
35            }
36            if ($day->day == $dayNumber && $day->month == $month && $day->year == $year) {
37                unset($this[$key]);
38                $this[$dateHash] = $day;
39                return $day;
40            }
41        }
42        if ($createDay) {
43            $day  = new \BO\Zmsentities\Day([
44                'year' => $year,
45                'month' => $month,
46                'day' => $dayNumber
47            ]);
48            $this[$dateHash] = $day;
49            return $day;
50        }
51        return null;
52    }
53
54    public function getDayByDateTime(\DateTimeInterface $datetime): \BO\Zmsentities\Day
55    {
56        return $this->getDay($datetime->format('Y'), $datetime->format('m'), $datetime->format('d'));
57    }
58
59    public function getDayByDay(\BO\Zmsentities\Day $day): \BO\Zmsentities\Day
60    {
61        return $this->getDay($day->year, $day->month, $day->day);
62    }
63
64
65    public function hasDay(mixed $year, mixed $month, mixed $dayNumber): bool
66    {
67        $day = $this->getDay($year, $month, $dayNumber, false);
68        return ($day === null) ? false : true;
69    }
70
71    public function withAssociatedDays(\DateTimeInterface $currentDate): self
72    {
73        $dayList = new self();
74        $lastDay = $currentDate->format('t');
75        for ($dayNumber = 1; $dayNumber <= $lastDay; $dayNumber++) {
76            $day = str_pad((string) $dayNumber, 2, '0', STR_PAD_LEFT);
77            $entity = $this->getDay($currentDate->format('Y'), $currentDate->format('m'), $day);
78            $dayList->addEntity($entity);
79        }
80        $dayList->sortByCustomKey('day');
81        return $dayList;
82    }
83
84    public function setStatusByType(mixed $slotType, \DateTimeInterface $dateTime): static
85    {
86        foreach ($this as $day) {
87            $day->getWithStatus($slotType, $dateTime);
88        }
89        return $this;
90    }
91
92    public function withAddedDayList(DayList $dayList): self
93    {
94        $merged = new DayList();
95        foreach ($dayList as $day) {
96            // @codeCoverageIgnoreStart
97            if (!$day instanceof Day) {
98                $day = new Day($day);
99            }
100            // @codeCoverageIgnoreEnd
101            $merged->addEntity($day->withAddedDay($this->getDayByDay($day)));
102        }
103        return $merged;
104    }
105
106    public function setSortByDate(): static
107    {
108        $this->uasort(function ($dayA, $dayB) {
109            return (
110                intval($dayA['year'] . $dayA['month'] . $dayA['day']) -
111                intval($dayB['year'] . $dayB['month'] . $dayB['day'])
112            );
113        });
114        return $this;
115    }
116
117    public function setSort(string $property = 'day'): static
118    {
119        $this->uasort(function ($dayA, $dayB) use ($property) {
120            return strnatcmp($dayA[$property], $dayB[$property]);
121        });
122        return $this;
123    }
124
125    public function hasDayWithAppointments(): bool
126    {
127        foreach ($this as $day) {
128            $day = new Day($day);
129            if ($day->hasAppointments()) {
130                return true;
131            }
132        }
133        return false;
134    }
135
136    public function getFirstBookableDay(): mixed
137    {
138        foreach ($this as $day) {
139            $day = new Day($day);
140            if ($day->isBookable()) {
141                return $day->toDateTime();
142            }
143        }
144        return null;
145    }
146
147    public function withDaysInDateRange(\DateTimeInterface $startDate, \DateTimeInterface $endDate): self
148    {
149        $list = new self();
150        $rangeStart = \BO\Zmsentities\Helper\DateTime::create($startDate)->modify('00:00:00');
151        $rangeEnd = \BO\Zmsentities\Helper\DateTime::create($endDate)->modify('23:59:59');
152        foreach ($this as $day) {
153            if (
154                $day->toDateTime() >= $rangeStart &&
155                $day->toDateTime() <= $rangeEnd
156            ) {
157                $list->addEntity($day);
158            }
159        }
160        return $list;
161    }
162
163    public function withDaysFromPeriod(\DateTimeInterface $startDate, \DateTimeInterface $endDate): self
164    {
165        $list = new self();
166        $startDate = \BO\Zmsentities\Helper\DateTime::create($startDate);
167        do {
168            $day = (new Day())->setDateTime($startDate);
169            $list->addEntity($day);
170            $startDate = $startDate->modify('+1 day');
171        } while ($startDate <= $endDate);
172        return $list;
173    }
174}

Inherited from BO\Zmsentities\Collection\Base

32    public function __construct(object|array $array = [], int $flags = 0, string $iteratorClass = "ArrayIterator")
33    {
34        /** @var class-string<\ArrayIterator> $iteratorClass */
35        parent::__construct($array, $flags, $iteratorClass);
36    }
41    public function getFirst()
42    {
43        $item = $this->getIterator()->current();
44        return $item;
45    }
51    public function getLast()
52    {
53        $copy = $this->getArrayCopy();
54        $item = end($copy);
55        return $item;
56    }
62    public function sortByName()
63    {
64        $this->uasort(function ($a, $b) {
65            return strcmp(
66                Sorter::toSortableString(ucfirst($a->name ?? '')),
67                Sorter::toSortableString(ucfirst($b->name ?? ''))
68            );
69        });
70        return $this;
71    }
73    public function sortByContactName(): static
74    {
75        $this->uasort(function ($a, $b) {
76            return strcmp(
77                Sorter::toSortableString(ucfirst($a->contact['name'] ?? '')),
78                Sorter::toSortableString(ucfirst($b->contact['name'] ?? ''))
79            );
80        });
81        return $this;
82    }
84    public function sortByCustomKey(string $key): static
85    {
86        $this->uasort(function ($a, $b) use ($key): mixed {
87            return ($a[$key] - $b[$key]);
88        });
89        return $this;
90    }
92    public function sortByCustomStringKey(mixed $key): static
93    {
94        $this->uasort(function ($a, $b) use ($key) {
95            return strcmp(
96                Sorter::toSortableString(ucfirst($a[$key] ?? '')),
97                Sorter::toSortableString(ucfirst($b[$key] ?? ''))
98            );
99        });
100        return $this;
101    }
103    public function __clone()
104    {
105        foreach ($this as $key => $property) {
106            $this[$key] = clone $property;
107        }
108    }
110    public function hasEntity(mixed $primary): bool
111    {
112        foreach ($this as $entity) {
113            if (isset($entity->{$entity::PRIMARY}) && $primary == $entity->{$entity::PRIMARY}) {
114                return true;
115            }
116        }
117        return false;
118    }
124    public function getEntity(string|int $primary)
125    {
126        foreach ($this as $entity) {
127            if (isset($entity->{$entity::PRIMARY}) && $primary == $entity->{$entity::PRIMARY}) {
128                return $entity;
129            }
130        }
131        return null;
132    }
134    public function addEntity(\BO\Zmsentities\Schema\Entity $entity): static
135    {
136        /** @var T $entity */
137        $this->append($entity);
138        return $this;
139    }
146    public function offsetSet(mixed $index, mixed $value): void
147    {
148        $className = $this::ENTITY_CLASS;
149        if (is_a($value, $className)) {
150            /** @var T $entity */
151            $entity = $value;
152            /** @psalm-suppress PossiblyNullArgument */
153            parent::offsetSet($index, $entity);
154        } elseif (is_array($value)) {
155            /** @var T $entity */
156            $entity = new $className($value);
157            /** @psalm-suppress PossiblyNullArgument */
158            parent::offsetSet($index, $entity);
159        } else {
160            $given = get_debug_type($value);
161            throw new \Exception('Invalid entity ' . $given . ' for collection ' . __CLASS__);
162        }
163    }
165    public function addData(mixed $mergeData): static
166    {
167        foreach ($mergeData as $item) {
168            if ($item instanceof Entity) {
169                if (null === $item->getResolveLevel()) {
170                    $item->setResolveLevel($this->getResolveLevel());
171                }
172                $this->addEntity($item);
173            } else {
174                $className = $this::ENTITY_CLASS;
175                $entity = new $className($item);
176                if (!$entity instanceof Entity) {
177                    throw new \Exception('Invalid entity ' . get_debug_type($entity) . ' for collection ' . __CLASS__);
178                }
179                $entity->setResolveLevel($this->getResolveLevel());
180                $this->addEntity($entity);
181            }
182        }
183        return $this;
184    }
189    public function addList(Base $list): static
190    {
191        foreach ($list as $item) {
192            $this->addEntity($item);
193        }
194        return $this;
195    }
201    public function getIds()
202    {
203        $list = [];
204        foreach ($this as $entity) {
205            $list[] = $entity->id;
206        }
207        return $list;
208    }
210    public function getIdsCsv(): string
211    {
212        return implode(',', $this->getIds());
213    }
215    public function getCsvForProperty(mixed $propertyName, string $csvSeperator = ','): string
216    {
217        $list = [];
218        foreach ($this as $entry) {
219            if ($entry->hasProperty($propertyName)) {
220                $list[] = $entry->getProperty($propertyName);
221            }
222        }
223        return implode($csvSeperator, $list);
224    }
226    public function getCsvForPropertyList(array $propertyList, string $propertySeperator = '', string $csvSeperator = ','): string
227    {
228        $list = [];
229        foreach ($this as $entry) {
230            $propertyConcat = '';
231            foreach ($propertyList as $propertyName) {
232                if ($entry->hasProperty($propertyName)) {
233                    if ($propertyConcat) {
234                        $propertyConcat .= $propertySeperator;
235                    }
236                    $propertyConcat .= $entry->getProperty($propertyName);
237                }
238            }
239            if ($propertyConcat) {
240                $list[] = $propertyConcat;
241            }
242        }
243        return implode($csvSeperator, $list);
244    }
250    public function withValueFor(mixed $param, mixed $newValue): static
251    {
252        /** @psalm-suppress UnsafeGenericInstantiation */
253        $list = new static();
254        foreach ($this as $entry) {
255            $list[] = $entry->withProperty($param, $newValue);
256        }
257        return $list;
258    }
266    public function withLessData(array $keepArray = [])
267    {
268        /** @psalm-suppress UnsafeGenericInstantiation */
269        $list = new static();
270        foreach ($this as $key => $item) {
271            /** @psalm-suppress TooManyArguments */
272            $list[$key] = $item->withLessData($keepArray);
273        }
274        return $list;
275    }
277    public function setJsonCompressLevel(mixed $jsonCompressLevel): void
278    {
279        foreach ($this as $item) {
280            $item->setJsonCompressLevel($jsonCompressLevel);
281        }
282    }
288    public function getAsArray(): array
289    {
290        $array = [];
291        foreach ($this as $item) {
292            $array[] = $item;
293        }
294
295        return $array;
296    }
299    public function jsonSerialize(): mixed
300    {
301        return $this->getArrayCopy();
302    }
304    public function __toString()
305    {
306        $list = [];
307        foreach ($this as $item) {
308            $list[] = $item->__toString();
309        }
310        return "[" . implode(',', $list) . "]";
311    }
316    public function getResolveLevel()
317    {
318        return $this->resolveLevel;
319    }
325    public function setResolveLevel($resolveLevel)
326    {
327        $this->resolveLevel = $resolveLevel;
328        return $this;
329    }
335    public function withResolveLevel($resolveLevel)
336    {
337        /** @psalm-suppress UnsafeGenericInstantiation */
338        $collection = new static();
339        foreach ($this as $entity) {
340            $reduced = $entity->withResolveLevel($resolveLevel);
341            $collection[] = $reduced;
342        }
343        return $collection;
344    }
350    public function chunk(mixed $length): array
351    {
352        /** @psalm-suppress UnsafeGenericInstantiation */
353        $chunks = [new static()];
354        $id = 0;
355        foreach ($this as $entry) {
356            $chunkIndex = (int) floor($id / $length);
357            if (! isset($chunks[$chunkIndex])) {
358                /** @psalm-suppress UnsafeGenericInstantiation */
359                $chunks[$chunkIndex] = new static();
360            }
361
362            $chunks[$chunkIndex][] = $entry;
363
364            $id++;
365        }
366
367        return $chunks;
368    }