Enums

Тільки цільові значення

  • Додано у версії: PHP 8.1
  • Що дає: нативний механізм для створення перелічуваних типів — із чіткими, типізованими значеннями замість "магічних рядків та чисел" і нескінченних const.

Трохи особистого

Це один із моїх найулюбленіших інструментів у PHP. З моменту їх появи я взагалі не уявляю розробки без enum’ів — вони зменшують кількість помилок, роблять код читабельним і гарантують типобезпеку.

Але спілкування з розробниками показує, що далеко не всі розділяють цей захват. Тому хочу поділитися своєю любов’ю до них і пояснити, чому це не просто "список значень", а справжній крок вперед для PHP.

Типи enum’ів

PHP має два різновиди enum’ів.

1. Backed Enum — із базовим значенням (string або int)

Кожен case має прив’язане значення, це дозволяє порівнювати його з іншими значеннями, серіалізувати, зберігати його в БД, та при цьому бути впевненим, що значення завжди буде одним з дозволених в переліченні.

enum Status: string 
{
    case DRAFT = 'draft';
    case ACTIVE = 'active';
    case ARCHIVED = 'archived';
}

Для чого:

  • зручно в статусах замовлень, документів, платежів, типів повідомлень, типів працівників, тощо;
  • легко зберігати в базі як звичайний рядок;
  • можна отримати enum через Status::from('draft') або безпечно — tryFrom('draft').

2. Pure Enum — без бекінгу

Просто набір унікальних case без значень. Використовується тоді, коли значення не потрібне — важливий сам факт "стану" або "режиму".

enum Direction 
{
    case NORTH;
    case SOUTH;
    case EAST;
    case WEST;
}

Для чого:

  • для внутрішніх логічних станів або режимів роботи;
  • коли не треба нічого зберігати у БД;
  • коли досить просто перевірити if ($direction === Direction::NORTH).

Більше, ніж просто перелік

Більшість вважає, що enum — це просто заміна const. Насправді це класоподібна конструкція, у якій можна (і треба!) додавати методи, поведінку, статичні методи та перетворення значень.

Приклад enum із методами

enum Status: string 
{
    case DRAFT = 'draft';
    case ACTIVE = 'active';
    case ARCHIVED = 'archived';

    // 🔹 Нестатичний метод — для конкретного значення
    public function label(): string 
    {
        return match($this) {
            self::DRAFT => 'Чернетка',
            self::ACTIVE => 'Активна',
            self::ARCHIVED => 'Архівована',
        };
    }

    // 🔹 Статичний метод — спільний для всіх
    public static function visible(): array 
    {
        return [self::DRAFT, self::ACTIVE];
    }
}

echo Status::ACTIVE->label(); // Активна
print_r(Status::visible());   // [Status::DRAFT, Status::ACTIVE]

Інший приклад

Статуси документів, каналів, ролей:

enum Channel: string 
{
    case EMAIL = 'email';
    case SMS = 'sms';
    case TELEGRAM = 'telegram';

    public function icon(): string 
    {
        return match($this) {
            self::EMAIL => '✉️',
            self::SMS => '📱',
            self::TELEGRAM => '💬',
        };
    }

    public function getSenderFQCN(): string 
    {
        return match($this) {
            self::EMAIL => EmailSender::class,
            self::SMS => SMSSender::class,
            self::TELEGRAM => TelegramSender::class,
        };
    }

    public static function fromString(string $value): ?self 
    {
        return self::tryFrom($value);
    }
}

Висновок

Enum’и — це одне із найвдаліших оновлень в PHP за останні роки. Вони дисциплінують код, роблять модель даних передбачуваною й забезпечують реальну типобезпеку.

Я вже не уявляю розробки без enum’ів. І якщо ти досі не використовуєш їх у своїх проєктах — просто спробуй, через тиждень без них ти вже не зможеш писати старим способом.

Doctrine + Enum

Я так люблю enum’и, що зробив навіть власний пакет — ufo-tech/doctrine-helper, який навчає Doctrine розуміти enum у типізованих полях сутності і створювати під них ENUM() тип поля в MySQL.

1. Створи свій PHP Enum для поля сутності

<?php
namespace App;

enum MyEnum: string
{
    case CASE_1 = 'Мій варіант 1';
    case CASE_2 = 'Мій варіант 2';
}

2. Створи власний DBAL-тип і наслідуй Ufo\DoctrineHelper\DBAL\AbstractEnumType

<?php
namespace App\DBAL;

use BackedEnum;
use App\MyEnum;

class MyEnumType extends AbstractEnumType
{
    protected string|BackedEnum $enum = MyEnum::class;
}

3. Зареєструй новий тип у Doctrine

doctrine:
    dbal:
        types:
            App\MyEnum: 'App\DBAL\MyEnumType'
        mapping_types:
            enum: string

4. Використовуй PHP Enum для зберігання й зміни значення

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use App\MyEnum;

#[ORM\Entity]
#[ORM\Table(name: 'demo')]
class Demo
{
    #[ORM\Column(type: MyEnum::class, nullable: false)]
    protected string $case;

    public function __construct(MyEnum $case)
    {
        $this->case = $case->value;
    }

    public function changeCase(MyEnum $case): void
    {
        $this->case = $case->value;
    }

    public function getCase(): string
    {
        return $this->case;
    }
}

Таким чином MySQL ENUM поводиться як повноцінний PHP Enum — із типобезпекою, автопідказками IDE та без ручного кастингу.

← Попередня: Іменовані аргументи · Уся серія · Наступна: readonly + accessors →


Вперше опубліковано в Telegram-каналі «Створений щоб помирати» 24.10.2025; розділ «Doctrine + Enum» — окремим постом того ж дня.