Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама
Senior Enums

Як описати переходи між статусами за допомогою enum?

Статус замовлення, заявки чи статті рідко може змінитися на будь-який інший: оплачене не стає новим, відправлене не скасовується. Ці правила - машина станів, і enum - зручне місце, щоб описати її в одному місці.

enum OrderStatus: string
{
    case New = 'new';
    case Paid = 'paid';
    case Shipped = 'shipped';
    case Cancelled = 'cancelled';

    /** @return list<self> */
    public function allowedTransitions(): array
    {
        return match ($this) {
            self::New => [self::Paid, self::Cancelled],
            self::Paid => [self::Shipped, self::Cancelled],
            self::Shipped, self::Cancelled => [],
        };
    }

    public function canTransitionTo(self $next): bool
    {
        return in_array($next, $this->allowedTransitions(), true);
    }
}

Застосування в моделі:

public function transitionTo(OrderStatus $next): void
{
    if (! $this->status->canTransitionTo($next)) {
        throw new InvalidStateTransition($this->status, $next);
    }

    $this->update(['status' => $next]);
    event(new OrderStatusChanged($this, $next));
}

Що це дає:

  • Правила переходів - в одному місці, а не розкидані if-ами по контролерах.
  • match без default змушує описати переходи для кожного нового статусу.
  • Легко тестувати: таблиця «з якого - в який - дозволено».
  • UI може показувати лише доступні дії: $order->status->allowedTransitions().

Коли enum уже замало: переходи залежать від даних (оплатити можна, лише якщо сума збігається), потрібні дії при вході й виході зі стану, історія переходів, паралельні стани. Тоді - окремі класи станів (патерн State) чи бібліотеки на кшталт spatie/laravel-model-states, а для довгих бізнес-процесів - workflow-рушії.

Конкурентність: перевірка й оновлення мають бути атомарними - UPDATE ... SET status = 'paid' WHERE id = ? AND status = 'new' чи блокування рядка, інакше два паралельні запити обидва «побачать» статус New.

Докладніше в документації: Методи перелічень

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання