Enumy weszły do PHP w wersji 8.1 i są czymś więcej niż tylko typowaną listą stałych. Backed enums z wartościami string lub int, metody na enumach, implementacja interfejsów, cases jako argumenty funkcji – to narzędzia które eliminują całą klasę błędów związanych z magic strings i niezwalidowanymi wartościami. Pokażę wszystkie możliwości z praktycznymi przykładami z Magento 2.
Pure enum vs Backed enum
<?php
declare(strict_types=1);
// Pure enum - tylko nazwy, bez wartości
enum Direction
{
case North;
case South;
case East;
case West;
}
// Backed enum int - case ma przypisaną wartość int
enum HttpStatus: int
{
case Ok = 200;
case Created = 201;
case NoContent = 204;
case BadRequest = 400;
case Unauthorized = 401;
case Forbidden = 403;
case NotFound = 404;
case UnprocessableEntity = 422;
case InternalServerError = 500;
}
// Backed enum string - case ma przypisaną wartość string
enum OrderStatus: string
{
case Pending = 'pending';
case Processing = 'processing';
case Complete = 'complete';
case Cancelled = 'cancelled';
case Closed = 'closed';
case Holded = 'holded';
}
// Konwersja z/do wartości backing
$status = OrderStatus::from('complete'); // OrderStatus::Complete
$status = OrderStatus::tryFrom('unknown'); // null (nie rzuca wyjątku)
$value = OrderStatus::Complete->value; // 'complete'
$name = OrderStatus::Complete->name; // 'Complete'
// Lista wszystkich cases
$allStatuses = OrderStatus::cases();
// [OrderStatus::Pending, OrderStatus::Processing, ...]
Metody na enumach
<?php
declare(strict_types=1);
enum OrderStatus: string
{
case Pending = 'pending';
case Processing = 'processing';
case Complete = 'complete';
case Cancelled = 'cancelled';
case Holded = 'holded';
// Etykieta do wyświetlenia w UI
public function label(): string
{
return match($this) {
self::Pending => 'Oczekujące',
self::Processing => 'W realizacji',
self::Complete => 'Zakończone',
self::Cancelled => 'Anulowane',
self::Holded => 'Wstrzymane',
};
}
// Kolor dla badge w UI
public function color(): string
{
return match($this) {
self::Pending => 'yellow',
self::Processing => 'blue',
self::Complete => 'green',
self::Cancelled => 'red',
self::Holded => 'gray',
};
}
// Dozwolone przejścia statusów
public function allowedTransitions(): array
{
return match($this) {
self::Pending => [self::Processing, self::Cancelled],
self::Processing => [self::Complete, self::Holded, self::Cancelled],
self::Holded => [self::Processing, self::Cancelled],
self::Complete,
self::Cancelled => [],
};
}
public function canTransitionTo(self $newStatus): bool
{
return in_array($newStatus, $this->allowedTransitions(), true);
}
// Czy to status końcowy
public function isFinal(): bool
{
return empty($this->allowedTransitions());
}
}
// Użycie
$status = OrderStatus::Processing;
echo $status->label(); // "W realizacji"
echo $status->color(); // "blue"
if ($status->canTransitionTo(OrderStatus::Complete)) {
// dozwolone przejście
}
// Walidacja przy zmianie statusu
$current = OrderStatus::Pending;
$next = OrderStatus::Complete;
if (!$current->canTransitionTo($next)) {
throw new \RuntimeException(
"Niedozwolone przejście: {$current->label()} => {$next->label()}"
);
}
Enumeracje implementujące interfejsy
<?php
declare(strict_types=1);
interface HasLabel
{
public function label(): string;
}
interface HasIcon
{
public function icon(): string;
}
// Enum może implementować wiele interfejsów
enum PaymentMethod: string implements HasLabel, HasIcon
{
case CreditCard = 'checkmo';
case BankTransfer= 'banktransfer';
case PayPal = 'paypal_express';
case CashOnDelivery = 'cashondelivery';
public function label(): string
{
return match($this) {
self::CreditCard => 'Karta kredytowa',
self::BankTransfer => 'Przelew bankowy',
self::PayPal => 'PayPal',
self::CashOnDelivery => 'Płatność przy odbiorze',
};
}
public function icon(): string
{
return match($this) {
self::CreditCard => 'icon-credit-card',
self::BankTransfer => 'icon-bank',
self::PayPal => 'icon-paypal',
self::CashOnDelivery => 'icon-cash',
};
}
public function requiresOnlineCapture(): bool
{
return match($this) {
self::CreditCard, self::PayPal => true,
default => false,
};
}
}
// Funkcja akceptuje interfejs - działa z każdym enum który go implementuje
function renderPaymentBadge(HasLabel&HasIcon $method): string
{
return sprintf(
'%s',
$method->icon(),
$method->label()
);
}
echo renderPaymentBadge(PaymentMethod::PayPal);
Stałe na enumach
<?php
declare(strict_types=1);
enum Priority: int
{
case Low = 1;
case Medium = 5;
case High = 10;
case Critical = 100;
// Stałe na enumie (PHP 8.3+: można typować)
const int DEFAULT = 5;
const int MIN = 1;
const int MAX = 100;
// Stała może być casem
const self FALLBACK = self::Low;
public function isUrgent(): bool
{
return $this->value >= self::High->value;
}
}
echo Priority::DEFAULT; // 5
echo Priority::FALLBACK->label; // błąd - to nie jest enum z metodą label
Priority::from(Priority::DEFAULT); // Priority::Medium
Enumeracje w Magento 2
<?php
declare(strict_types=1);
// Zastąp magic strings w obserwatorach i pluginach
enum MagentoEvent: string
{
case OrderPlaced = 'sales_order_place_after';
case OrderCancelled = 'order_cancel_after';
case ProductSaved = 'catalog_product_save_after';
case CustomerLogin = 'customer_login';
case CheckoutSubmitAll = 'checkout_submit_all_after';
}
// W etc/events.xml możesz odwoływać się do wartości:
// Nie możesz użyć PHP enum bezpośrednio w XML - ale w obserwatorze:
class OrderPlacedObserver implements \Magento\Framework\Event\ObserverInterface
{
public function execute(\Magento\Framework\Event\Observer $observer): void
{
// Zamiast 'sales_order_place_after' jako string - masz IDE support
$eventName = MagentoEvent::OrderPlaced->value;
}
}
// Enum dla typów produktów
enum ProductType: string
{
case Simple = 'simple';
case Configurable= 'configurable';
case Bundle = 'bundle';
case Virtual = 'virtual';
case Downloadable= 'downloadable';
case Grouped = 'grouped';
public function isPhysical(): bool
{
return match($this) {
self::Simple, self::Configurable, self::Bundle, self::Grouped => true,
self::Virtual, self::Downloadable => false,
};
}
public function supportsQty(): bool
{
return $this !== self::Grouped;
}
}
// Użycie w serwisie
class ProductProcessor
{
public function process(\Magento\Catalog\Api\Data\ProductInterface $product): void
{
$type = ProductType::from($product->getTypeId());
if ($type->isPhysical()) {
$this->calculateShipping($product);
}
if (!$type->supportsQty()) {
return; // grupowane nie mają własnego qty
}
$this->updateInventory($product);
}
}
Enum w tablicach i kolekcjach
<?php
// Enum jako klucz tablicy (PHP 8.1+)
$labels = [
OrderStatus::Pending => 'Oczekuje na płatność',
OrderStatus::Processing => 'Realizacja w toku',
OrderStatus::Complete => 'Wysłane',
];
echo $labels[OrderStatus::Pending]; // "Oczekuje na płatność"
// Filtrowanie kolekcji po enumie
$orders = $this->orderRepository->getList($searchCriteria)->getItems();
$pending = array_filter(
$orders,
fn($order) => OrderStatus::tryFrom($order->getStatus()) === OrderStatus::Pending
);
// Grupowanie po statusie
$grouped = [];
foreach ($orders as $order) {
$status = OrderStatus::tryFrom($order->getStatus()) ?? OrderStatus::Pending;
$grouped[$status->value][] = $order;
}
Podsumowanie
Enumy PHP 8.1 to więcej niż typowane stałe. Backed enums eliminują magic strings i zapewniają bezpieczną konwersję z/do wartości bazowej. Metody na enumach przenoszą logikę związaną z danym typem do miejsca gdzie powinna być. Interfejsy na enumach umożliwiają polimorfizm. W Magento 2 enumy najlepiej sprawdzają się wszędzie tam gdzie dotychczas używałeś class-of-constants – statusy zamówień, typy produktów, kody metod płatności i wysyłki.
