CasesCollection   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 330
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 14
Bugs 0 Features 1
Metric Value
wmc 46
eloc 61
c 14
b 0
f 1
dl 0
loc 330
ccs 86
cts 86
cp 1
rs 8.72

28 Methods

Rating   Name   Duplication   Size   Complexity  
A values() 0 4 1
A sortByDescValue() 0 3 2
A __toString() 0 3 1
A first() 0 11 3
A toArray() 0 9 3
A names() 0 4 1
A getIterator() 0 3 1
A filter() 0 5 2
A keyByName() 0 3 1
A except() 0 3 1
A keyByValue() 0 3 2
A only() 0 3 1
A sortDesc() 0 3 1
A sort() 0 3 1
A onlyValues() 0 3 2
A all() 0 3 1
A groupBy() 0 14 3
A jsonSerialize() 0 3 2
A pluck() 0 13 3
A sortBy() 0 7 1
A has() 0 9 3
A map() 0 6 1
A count() 0 3 1
A exceptValues() 0 3 2
A keyBy() 0 9 2
A sortByDesc() 0 7 1
A sortByValue() 0 3 2
A __construct() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like CasesCollection often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CasesCollection, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cerbero\Enum;
6
7
use BackedEnum;
8
use Countable;
9
use IteratorAggregate;
10
use JsonSerializable;
11
use Stringable;
12
use Traversable;
13
use UnitEnum;
14
15
/**
16
 * The collection of enum cases.
17
 *
18
 * @template-covariant TEnum of UnitEnum
19
 *
20
 * @implements IteratorAggregate<array-key, TEnum>
21
 */
22
class CasesCollection implements Countable, IteratorAggregate, JsonSerializable, Stringable
23
{
24
    /**
25
     * Whether the cases belong to a backed enum.
26
     */
27
    protected readonly bool $enumIsBacked;
28
29
    /**
30
     * Instantiate the class.
31
     *
32
     * @param array<array-key, TEnum> $cases
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, TEnum> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, TEnum>.
Loading history...
33
     */
34 68
    final public function __construct(protected readonly array $cases)
35
    {
36 68
        $this->enumIsBacked = reset($cases) instanceof BackedEnum;
37
    }
38
39
    /**
40
     * Turn the collection into a string.
41
     */
42 2
    public function __toString(): string
43
    {
44 2
        return (string) json_encode($this->jsonSerialize());
45
    }
46
47
    /**
48
     * Turn the collection into a JSON serializable array.
49
     *
50
     * @return list<string|int>
51
     */
52 2
    public function jsonSerialize(): array
53
    {
54 2
        return $this->enumIsBacked ? $this->values() : $this->names();
55
    }
56
57
    /**
58
     * Retrieve the count of cases.
59
     */
60 4
    public function count(): int
61
    {
62 4
        return count($this->cases);
63
    }
64
65
    /**
66
     * Retrieve the iterable cases.
67
     *
68
     * @return Traversable<array-key, TEnum>
69
     */
70 4
    public function getIterator(): Traversable
71
    {
72 4
        yield from $this->cases;
73
    }
74
75
    /**
76
     * Retrieve all the cases as a plain array.
77
     *
78
     * @return array<array-key, TEnum>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, TEnum> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, TEnum>.
Loading history...
79
     */
80 73
    public function all(): array
81
    {
82 73
        return $this->cases;
83
    }
84
85
    /**
86
     * Determine whether the collection contains the given case.
87
     */
88 2
    public function has(mixed $case): bool
89
    {
90 2
        foreach ($this->cases as $instance) {
91 2
            if ($instance->is($case)) {
92 2
                return true;
93
            }
94
        }
95
96 2
        return false;
97
    }
98
99
    /**
100
     * Retrieve all the cases as a plain array recursively.
101
     *
102
     * @return array<array-key, mixed>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, mixed> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>.
Loading history...
103
     */
104 1
    public function toArray(): array
105
    {
106 1
        $array = [];
107
108 1
        foreach ($this->cases as $key => $value) {
109 1
            $array[$key] = $value instanceof static ? $value->toArray() : $value;
110
        }
111
112 1
        return $array;
113
    }
114
115
    /**
116
     * Retrieve the first case.
117
     *
118
     * @param ?callable(TEnum, array-key): bool $callback
119
     * @return ?TEnum
120
     */
121 5
    public function first(?callable $callback = null): mixed
122
    {
123 5
        $callback ??= fn() => true;
124
125 5
        foreach ($this->cases as $key => $case) {
126 4
            if ($callback($case, $key)) {
127 4
                return $case;
128
            }
129
        }
130
131 1
        return null;
132
    }
133
134
    /**
135
     * Retrieve all the names of the cases.
136
     *
137
     * @return list<string>
138
     */
139 3
    public function names(): array
140
    {
141
        /** @var list<string> */
142 3
        return array_column($this->cases, 'name');
143
    }
144
145
    /**
146
     * Retrieve all the values of the backed cases.
147
     *
148
     * @return list<string|int>
149
     */
150 4
    public function values(): array
151
    {
152
        /** @var list<string|int> */
153 4
        return array_column($this->cases, 'value');
154
    }
155
156
    /**
157
     * Retrieve an array of values optionally keyed by the given key.
158
     *
159
     * @template TPluckValue
160
     *
161
     * @param (callable(TEnum): TPluckValue)|string $value
0 ignored issues
show
Documentation Bug introduced by
The doc comment (callable(TEnum): TPluckValue)|string at position 1 could not be parsed: Expected ')' at position 1, but found 'callable'.
Loading history...
162
     * @param (callable(TEnum): array-key)|string|null $key
163
     * @return array<array-key, TPluckValue>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, TPluckValue> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, TPluckValue>.
Loading history...
164
     */
165 10
    public function pluck(callable|string $value, callable|string|null $key = null): array
166
    {
167 10
        $result = [];
168
169 10
        foreach ($this->cases as $case) {
170 10
            if ($key === null) {
171 6
                $result[] = $case->resolveItem($value);
172
            } else {
173 4
                $result[$case->resolveItem($key)] = $case->resolveItem($value);
174
            }
175
        }
176
177 10
        return $result;
178
    }
179
180
    /**
181
     * Retrieve the result of mapping over the cases.
182
     *
183
     * @template TMapValue
184
     *
185
     * @param callable(TEnum, array-key): TMapValue $callback
186
     * @return array<array-key, TMapValue>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, TMapValue> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, TMapValue>.
Loading history...
187
     */
188 3
    public function map(callable $callback): array
189
    {
190 3
        $keys = array_keys($this->cases);
191 3
        $values = array_map($callback, $this->cases, $keys);
192
193 3
        return array_combine($keys, $values);
194
    }
195
196
    /**
197
     * Retrieve the cases keyed by their own name.
198
     */
199 1
    public function keyByName(): static
200
    {
201 1
        return $this->keyBy('name');
202
    }
203
204
    /**
205
     * Retrieve the cases keyed by the given key.
206
     *
207
     * @param (callable(TEnum): array-key)|string $key
0 ignored issues
show
Documentation Bug introduced by
The doc comment (callable(TEnum): array-key)|string at position 1 could not be parsed: Expected ')' at position 1, but found 'callable'.
Loading history...
208
     */
209 4
    public function keyBy(callable|string $key): static
210
    {
211 4
        $keyed = [];
212
213 4
        foreach ($this->cases as $case) {
214 4
            $keyed[$case->resolveItem($key)] = $case;
215
        }
216
217 4
        return new static($keyed);
218
    }
219
220
    /**
221
     * Retrieve the cases keyed by their own value.
222
     */
223 2
    public function keyByValue(): static
224
    {
225 2
        return $this->enumIsBacked ? $this->keyBy('value') : new static([]);
226
    }
227
228
    /**
229
     * Retrieve the cases grouped by the given key.
230
     *
231
     * @param (callable(TEnum): array-key)|string $key
0 ignored issues
show
Documentation Bug introduced by
The doc comment (callable(TEnum): array-key)|string at position 1 could not be parsed: Expected ')' at position 1, but found 'callable'.
Loading history...
232
     * @return array<array-key, static<TEnum>>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, static<TEnum>> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, static<TEnum>>.
Loading history...
233
     */
234 6
    public function groupBy(callable|string $key): array
235
    {
236 6
        $grouped = [];
237
238 6
        foreach ($this->cases as $case) {
239 6
            $grouped[$case->resolveItem($key)][] = $case;
240
        }
241
242 6
        foreach ($grouped as $key => $cases) {
243 6
            $grouped[$key] = new static($cases);
244
        }
245
246
        /** @var array<array-key, static<TEnum>> */
247 6
        return $grouped;
248
    }
249
250
    /**
251
     * Retrieve a new collection with the filtered cases.
252
     *
253
     * @param (callable(TEnum): bool)|string $filter
0 ignored issues
show
Documentation Bug introduced by
The doc comment (callable(TEnum): bool)|string at position 1 could not be parsed: Expected ')' at position 1, but found 'callable'.
Loading history...
254
     */
255 15
    public function filter(callable|string $filter): static
256
    {
257 15
        $callback = is_callable($filter) ? $filter : fn(UnitEnum $case) => $case->resolveItem($filter) === true;
258
259 15
        return new static(array_filter($this->cases, $callback));
260
    }
261
262
    /**
263
     * Retrieve a new collection of cases having only the given names.
264
     */
265 2
    public function only(string ...$name): static
266
    {
267 2
        return $this->filter(fn(UnitEnum $case) => in_array($case->name, $name));
268
    }
269
270
    /**
271
     * Retrieve a collection of cases not having the given names.
272
     */
273 2
    public function except(string ...$name): static
274
    {
275 2
        return $this->filter(fn(UnitEnum $case) => !in_array($case->name, $name));
276
    }
277
278
    /**
279
     * Retrieve a new collection of backed cases having only the given values.
280
     */
281 3
    public function onlyValues(string|int ...$value): static
282
    {
283 3
        return $this->filter(fn(UnitEnum $case) => $this->enumIsBacked && in_array($case->value, $value, true));
284
    }
285
286
    /**
287
     * Retrieve a new collection of backed cases not having the given values.
288
     */
289 3
    public function exceptValues(string|int ...$value): static
290
    {
291 3
        return $this->filter(fn(UnitEnum $case) => $this->enumIsBacked && !in_array($case->value, $value, true));
292
    }
293
294
    /**
295
     * Retrieve a new collection of cases sorted by their own name ascending.
296
     */
297 2
    public function sort(): static
298
    {
299 2
        return $this->sortBy('name');
300
    }
301
302
    /**
303
     * Retrieve a new collection of cases sorted by the given key ascending.
304
     *
305
     * @param (callable(TEnum): mixed)|string $key
0 ignored issues
show
Documentation Bug introduced by
The doc comment (callable(TEnum): mixed)|string at position 1 could not be parsed: Expected ')' at position 1, but found 'callable'.
Loading history...
306
     */
307 6
    public function sortBy(callable|string $key): static
308
    {
309 6
        $cases = $this->cases;
310
311 6
        uasort($cases, fn(UnitEnum $a, UnitEnum $b) => $a->resolveItem($key) <=> $b->resolveItem($key));
312
313 6
        return new static($cases);
314
    }
315
316
    /**
317
     * Retrieve a new collection of cases sorted by their own value ascending.
318
     */
319 2
    public function sortByValue(): static
320
    {
321 2
        return $this->enumIsBacked ? $this->sortBy('value') : new static([]);
322
    }
323
324
    /**
325
     * Retrieve a new collection of cases sorted by their own name descending.
326
     */
327 2
    public function sortDesc(): static
328
    {
329 2
        return $this->sortByDesc('name');
330
    }
331
332
    /**
333
     * Retrieve a new collection of cases sorted by the given key descending.
334
     *
335
     * @param (callable(TEnum): mixed)|string $key
0 ignored issues
show
Documentation Bug introduced by
The doc comment (callable(TEnum): mixed)|string at position 1 could not be parsed: Expected ')' at position 1, but found 'callable'.
Loading history...
336
     */
337 6
    public function sortByDesc(callable|string $key): static
338
    {
339 6
        $cases = $this->cases;
340
341 6
        uasort($cases, fn(UnitEnum $a, UnitEnum $b) => $b->resolveItem($key) <=> $a->resolveItem($key));
342
343 6
        return new static($cases);
344
    }
345
346
    /**
347
     * Retrieve a new collection of cases sorted by their own value descending.
348
     */
349 2
    public function sortByDescValue(): static
350
    {
351 2
        return $this->enumIsBacked ? $this->sortByDesc('value') : new static([]);
352
    }
353
}
354