← Enums

Ashterix

docs: English JsonRpcBundle docs, REST and MCP Bundle sections, package cards

uk/articles/from-tg/series/php-can-do/enums.md новий файл +217 −0

1 + # Enums
2 +
3 + *Тільки цільові значення*
4 +
5 + - **Додано у версії:** PHP 8.1
6 + - **Що дає:** нативний механізм для створення перелічуваних типів — із чіткими, типізованими значеннями замість "магічних рядків та чисел" і нескінченних const.
7 +
8 + ## Трохи особистого
9 +
10 + Це один із моїх найулюбленіших інструментів у PHP. З моменту їх появи я взагалі не уявляю розробки без enum’ів — вони зменшують кількість помилок, роблять код читабельним і гарантують типобезпеку.
11 +
12 + Але спілкування з розробниками показує, що далеко не всі розділяють цей захват. Тому хочу поділитися своєю любов’ю до них і пояснити, чому це не просто "список значень", а справжній крок вперед для PHP.
13 +
14 + ## Типи enum’ів
15 +
16 + PHP має два різновиди enum’ів.
17 +
18 + ### 1. Backed Enum — із базовим значенням (`string` або `int`)
19 +
20 + Кожен `case` має прив’язане значення, це дозволяє порівнювати його з іншими значеннями, серіалізувати, зберігати його в БД, та при цьому бути впевненим, що значення завжди буде одним з дозволених в переліченні.
21 +
22 + ```php
23 + enum Status: string
24 + {
25 + case DRAFT = 'draft';
26 + case ACTIVE = 'active';
27 + case ARCHIVED = 'archived';
28 + }
29 + ```
30 +
31 + Для чого:
32 +
33 + - зручно в статусах замовлень, документів, платежів, типів повідомлень, типів працівників, тощо;
34 + - легко зберігати в базі як звичайний рядок;
35 + - можна отримати enum через `Status::from('draft')` або безпечно — `tryFrom('draft')`.
36 +
37 + ### 2. Pure Enum — без бекінгу
38 +
39 + Просто набір унікальних case без значень. Використовується тоді, коли значення не потрібне — важливий сам факт "стану" або "режиму".
40 +
41 + ```php
42 + enum Direction
43 + {
44 + case NORTH;
45 + case SOUTH;
46 + case EAST;
47 + case WEST;
48 + }
49 + ```
50 +
51 + Для чого:
52 +
53 + - для внутрішніх логічних станів або режимів роботи;
54 + - коли не треба нічого зберігати у БД;
55 + - коли досить просто перевірити `if ($direction === Direction::NORTH)`.
56 +
57 + ## Більше, ніж просто перелік
58 +
59 + Більшість вважає, що `enum` — це просто заміна `const`. Насправді це класоподібна конструкція, у якій можна (і треба!) додавати методи, поведінку, статичні методи та перетворення значень.
60 +
61 + ### Приклад enum із методами
62 +
63 + ```php
64 + enum Status: string
65 + {
66 + case DRAFT = 'draft';
67 + case ACTIVE = 'active';
68 + case ARCHIVED = 'archived';
69 +
70 + // 🔹 Нестатичний метод — для конкретного значення
71 + public function label(): string
72 + {
73 + return match($this) {
74 + self::DRAFT => 'Чернетка',
75 + self::ACTIVE => 'Активна',
76 + self::ARCHIVED => 'Архівована',
77 + };
78 + }
79 +
80 + // 🔹 Статичний метод — спільний для всіх
81 + public static function visible(): array
82 + {
83 + return [self::DRAFT, self::ACTIVE];
84 + }
85 + }
86 +
87 + echo Status::ACTIVE->label(); // Активна
88 + print_r(Status::visible()); // [Status::DRAFT, Status::ACTIVE]
89 + ```
90 +
91 + ### Інший приклад
92 +
93 + Статуси документів, каналів, ролей:
94 +
95 + ```php
96 + enum Channel: string
97 + {
98 + case EMAIL = 'email';
99 + case SMS = 'sms';
100 + case TELEGRAM = 'telegram';
101 +
102 + public function icon(): string
103 + {
104 + return match($this) {
105 + self::EMAIL => '✉️',
106 + self::SMS => '📱',
107 + self::TELEGRAM => '💬',
108 + };
109 + }
110 +
111 + public function getSenderFQCN(): string
112 + {
113 + return match($this) {
114 + self::EMAIL => EmailSender::class,
115 + self::SMS => SMSSender::class,
116 + self::TELEGRAM => TelegramSender::class,
117 + };
118 + }
119 +
120 + public static function fromString(string $value): ?self
121 + {
122 + return self::tryFrom($value);
123 + }
124 + }
125 + ```
126 +
127 + ## Висновок
128 +
129 + > Enum’и — це одне із найвдаліших оновлень в PHP за останні роки.
130 + > Вони дисциплінують код, роблять модель даних передбачуваною й забезпечують реальну типобезпеку.
131 +
132 + Я вже не уявляю розробки без enum’ів. І якщо ти досі не використовуєш їх у своїх проєктах — просто спробуй, через тиждень без них ти вже не зможеш писати старим способом.
133 +
134 + ## Doctrine + Enum
135 +
136 + Я так люблю enum’и, що зробив навіть власний пакет — [ufo-tech/doctrine-helper](https://packagist.org/packages/ufo-tech/doctrine-helper), який навчає `Doctrine` розуміти `enum` у типізованих полях сутності і створювати під них `ENUM()` тип поля в `MySQL`.
137 +
138 + ### 1. Створи свій PHP Enum для поля сутності
139 +
140 + ```php
141 + <?php
142 + namespace App;
143 +
144 + enum MyEnum: string
145 + {
146 + case CASE_1 = 'Мій варіант 1';
147 + case CASE_2 = 'Мій варіант 2';
148 + }
149 + ```
150 +
151 + ### 2. Створи власний DBAL-тип і наслідуй `Ufo\DoctrineHelper\DBAL\AbstractEnumType`
152 +
153 + ```php
154 + <?php
155 + namespace App\DBAL;
156 +
157 + use BackedEnum;
158 + use App\MyEnum;
159 +
160 + class MyEnumType extends AbstractEnumType
161 + {
162 + protected string|BackedEnum $enum = MyEnum::class;
163 + }
164 + ```
165 +
166 + ### 3. Зареєструй новий тип у Doctrine
167 +
168 + ```yaml
169 + doctrine:
170 + dbal:
171 + types:
172 + App\MyEnum: 'App\DBAL\MyEnumType'
173 + mapping_types:
174 + enum: string
175 + ```
176 +
177 + ### 4. Використовуй PHP Enum для зберігання й зміни значення
178 +
179 + ```php
180 + <?php
181 +
182 + namespace App\Entity;
183 +
184 + use Doctrine\ORM\Mapping as ORM;
185 + use App\MyEnum;
186 +
187 + #[ORM\Entity]
188 + #[ORM\Table(name: 'demo')]
189 + class Demo
190 + {
191 + #[ORM\Column(type: MyEnum::class, nullable: false)]
192 + protected string $case;
193 +
194 + public function __construct(MyEnum $case)
195 + {
196 + $this->case = $case->value;
197 + }
198 +
199 + public function changeCase(MyEnum $case): void
200 + {
201 + $this->case = $case->value;
202 + }
203 +
204 + public function getCase(): string
205 + {
206 + return $this->case;
207 + }
208 + }
209 + ```
210 +
211 + Таким чином MySQL ENUM поводиться як повноцінний PHP Enum — із типобезпекою, автопідказками IDE та без ручного кастингу.
212 +
213 + ← [Попередня: Іменовані аргументи](/articles/from-tg/series/php-can-do/named-arguments) · [Уся серія](/articles/from-tg/series/php-can-do) · [Наступна: readonly + accessors →](/articles/from-tg/series/php-can-do/readonly)
214 +
215 + ---
216 +
217 + *Вперше опубліковано в Telegram-каналі [«Створений щоб помирати»](https://t.me/php_while_true/84) 24.10.2025; розділ «Doctrine + Enum» — [окремим постом](https://t.me/php_while_true/85) того ж дня.*