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) того ж дня.*