Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
55.70% covered (warning)
55.70%
44 / 79
40.00% covered (danger)
40.00%
6 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
BaseEnum
55.70% covered (warning)
55.70%
44 / 79
40.00% covered (danger)
40.00%
6 / 15
148.70
0.00% covered (danger)
0.00%
0 / 1
 value
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 label
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toLabels
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
5.02
 toValues
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 all
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toArray
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
20
 make
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 equals
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
5.39
 jsonSerialize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCache
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 setCache
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 initCache
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 getLabelsConstant
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getCustomDefinitions
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace App\Traits\Enums;
4
5use Illuminate\Support\Str;
6
7trait BaseEnum
8{
9    // * GENERIC METHODS
10    /**
11     * Get enum value.
12     *
13     * @return string|integer
14     */
15    public function value(): string|int
16    {
17        return self::toValues()[$this->name];
18    }
19
20    /**
21     * Get enum label.
22     *
23     * @return string
24     */
25    public function label(): string
26    {
27        return self::toLabels()[$this->name];
28    }
29
30    /**
31     * Get enum name.
32     *
33     * @return string
34     */
35    public function name(): string
36    {
37        return $this->name;
38    }
39
40    // * STATIC FETCH METHODS
41
42    /**
43     * Get enum labels.
44     *
45     * @return array<string>
46     */
47    public static function toLabels(): array
48    {
49        if (empty(self::getCache('labels'))) {
50            $labels = self::getLabelsConstant();
51            self::setCache('labels', \collect(self::cases())
52                ->mapWithKeys(function ($enum) use ($labels) {
53                    if (!\is_null($labels) and !isset($labels[$enum->name])) {
54                        throw new \Error('Missing definition label ' . self::class . " for {$enum->name}");
55                    }
56                    return [$enum->name => \trans(
57                        !\is_null($labels) ? $labels[$enum->name] : Str::ucfirst($enum->name)
58                    )];
59                })->all());
60        }
61        return self::getCache('labels');
62    }
63
64    /**
65     * Get enum values.
66     *
67     * @return array<integer|string>
68     */
69    public static function toValues(): array
70    {
71        if (empty(self::getCache('values'))) {
72            self::setCache('values', \collect(self::cases())
73                ->mapWithKeys(fn ($enum) => [$enum->name => $enum->value])->all());
74        }
75        return self::getCache('values');
76    }
77
78    /**
79     * Get enums.
80     *
81     * @return array
82     */
83    public static function all(): array
84    {
85        return self::cases();
86    }
87
88    /**
89     * Get enums.
90     *
91     * @return array
92     */
93    public static function toArray(): array
94    {
95        if (empty(self::getCache('enums'))) {
96            $definitions = self::getCustomDefinitions();
97            self::setCache('enums', \collect(self::cases())
98                ->mapWithKeys(function ($enum) use ($definitions) {
99                    $baseArray = [
100                        'name' => $enum->name,
101                        'label' => $enum->label(),
102                        'value' => $enum->value,
103                    ];
104                    foreach ($definitions as $index => $elements) {
105                        $index = Str::lower(Str::singular($index));
106                        if (!isset($elements[$enum->name])) {
107                            throw new \Error("Missing definition {$index} " . self::class . " for {$enum->name}");
108                        }
109                        $baseArray[$index] = $elements[$enum->name];
110                    }
111                    return [$enum->name => (object)$baseArray];
112                })->all());
113        }
114        return self::getCache('enums');
115    }
116
117    /**
118     * Fetch enum using value.
119     *
120     * @param \App\Traits\Enums\BaseEnum|integer|string $value
121     * @param string                                    $field
122     * @return \App\Traits\Enums\BaseEnum|null
123     */
124    public static function make(self|int|string $value, string $field = 'value'): self|null
125    {
126        return !\is_object($value) ?
127            \collect(self::cases())->keyBy($field)->get($value) : $value;
128    }
129
130    /**
131     * Test if 2 enum are equals.
132     *
133     * @param object ...$others
134     * @return boolean
135     * @throws \Error If provided element are not enums taht extends BaseEnum.
136     */
137    public function equals(object ...$others): bool
138    {
139        foreach ($others as $other) {
140            if (!\in_array(BaseEnum::class, \class_uses_recursive($other))) {
141                throw new \Error(__METHOD__ .
142                    ' requires $enum to use ' . BaseEnum::class);
143            }
144            if (
145                get_class($this) === get_class($other)
146                && $this->value === $other->value
147            ) {
148                return true;
149            }
150        }
151        return false;
152    }
153
154    /**
155     * Serialize to json.
156     *
157     * @return mixed
158     */
159    public function jsonSerialize(): mixed
160    {
161        return $this->value;
162    }
163
164
165    // * CACHE
166
167    /**
168     * Get cache array.
169     *
170     * @param string $index
171     * @return array
172     */
173    private static function getCache(string $index): array
174    {
175        self::initCache();
176        if (!isset($GLOBALS[self::class]['props'][$index])) {
177            $GLOBALS[self::class]['props'][$index] = [];
178        }
179        return $GLOBALS[self::class]['props'][$index];
180    }
181
182    /**
183     * Set cache array.
184     *
185     * @param string $index
186     * @param array  $items
187     * @return void
188     */
189    private static function setCache(string $index, array $items): void
190    {
191        self::initCache();
192        if (!isset($GLOBALS[self::class]['props'][$index])) {
193            $GLOBALS[self::class]['props'][$index] = [];
194        }
195        $GLOBALS[self::class]['props'][$index] = $items;
196    }
197
198    /**
199     * Init cache if needed.
200     *
201     * @return void
202     */
203    private static function initCache(): void
204    {
205        if (!isset($GLOBALS[self::class])) {
206            $GLOBALS[self::class] = [
207                'props' => [
208                    'enums' => [],
209                    'values' => [],
210                    'labels' => []
211                ],
212                'customProps' => null
213            ];
214        }
215    }
216
217    // * PRIVATE
218
219    /**
220     * Fetch the LABELS constant if exists.
221     *
222     * @return array|null
223     */
224    private static function getLabelsConstant(): array|null
225    {
226        $class_name = self::class;
227        try {
228            $constant_reflex = new \ReflectionClassConstant($class_name, 'LABELS');
229            return $constant_reflex->getValue();
230        } catch (\ReflectionException $e) {
231            return null;
232        }
233    }
234
235    /**
236     * Get custom constants.
237     *
238     * @return array
239     */
240    private static function getCustomDefinitions(): array
241    {
242        // ? Has custom props in cache ?
243        if (!isset($GLOBALS[self::class]) or \is_null($GLOBALS[self::class]['customProps'])) {
244            $names = \collect(self::cases())->keyBy('name');
245
246            $GLOBALS[self::class]['customProps'] = \collect((new \ReflectionClass(self::class))->getConstants())
247                ->filter(function (mixed $constant, string $key) use ($names) {
248                    if ($key === 'LABELS' or !\is_array($constant)) {
249                        return false;
250                    }
251                    return \collect($constant)->keys()
252                        ->filter(fn (string $name) => $names->has($name))->count();
253                })->all();
254        }
255        return $GLOBALS[self::class]['customProps'];
256    }
257}