ENUMS EN PHP: CÓMO FUNCIONAN Y EJEMPLOS

Una de las novedades de versión 8.1 de php es que se añade el tipo enums (enumerados) que son un tipo de datos definido por el usuario y que consiste en un conjunto de valores posibles. De igual manera que los enum en mysql. Los enums se construyen sobre clases y objetos. Se comportan de forma similar y tienen casi los mismos requisitos. Los enums comparten los mismos espacios de nombres que las clases, las interfaces y los traits.
Ejemplo declaración de enums en php:
enum tipoVehiculos
{
    case Coche;
    case Camion;
    case Moto;
    case Furgoneta;
}
//Para mostrar uno de los enums:
$vehiculo = tipoVehiculos::Coche;
var_dump(tipoVehiculos->name);
//Mostrará "Coche"
En el caso anterior se puede ver una declaración normal. Pero también se pueden definir backed Enums, o enums respaldados, si se quiere dar un valor equivalente a un escalar a cualquier caso. Sin embargo, los enums respaldados solo pueden tener un tipo, ya sea int o string pero nunca ambos.
enum tipoVehiculos2: string
{
    case Coche = 'Coche';
    case Camion = 'Camion';
    case Moto = 'Moto';
    case Furgoneta = 'Furgo';
}
//Para mostrar uno de los enums:
$vehiculo = tipoVehiculos2::Furgoneta;
var_dump(tipoVehiculos->value);
//Mostrará "Furgo"
Todos los casos diferentes de un enum respaldado deben tener un valor único. Y nunca puedes mezclar enums puros y respaldados.

Os invito a ver los enums en mysql.

Tipos de ENUMS

// 1. Enum básico (solo nombres)
enum Status
{
    case Pending;
    case Approved;
    case Rejected;
}

// 2. Enum Backed (el más usado - con valor)
enum OrderStatus: string
{
    case Pending   = 'pending';
    case Paid = 'paid';
    case Shipped   = 'shipped';
    case Delivered = 'delivered';
    case Cancelled = 'cancelled';
}

// 3. Enum con valor entero
enum Role: int
{
    case User  = 1;
    case Admin = 2;
    case Super = 3;
}

//USO
$status = OrderStatus::Pending;

// Comparaciones seguras
if ($order->status === OrderStatus::Paid) { ... }

// Con base de datos (el backed value)
$order->status = OrderStatus::Shipped->value; // 'shipped'

Métodos y lógica dentro del ENUM

enum GameStatus: string
{
    case Pending   = 'pending';
    case Joining   = 'joining';
    case InProgress = 'in_progress';
    case Completed = 'completed';
    case Cancelled = 'cancelled';

    //Método para saber si se puede cambiar de estado
    public function canTransitionTo(self $to): bool
    {
   return match ($this) {
  self::Pending     => in_array($to, [self::Joining, self::Cancelled]),
  self::Joining     => $to === self::InProgress,
  self::InProgress  => in_array($to, [self::Completed, self::Cancelled]),
  self::Completed,
  self::Cancelled   => false, // estados finales
   };
    }

    // Método extra
    public function isFinal(): bool
    {
   return in_array($this, [self::Completed, self::Cancelled]);
    }
}

//USO

$current = GameStatus::from($game->status);
if ($current->canTransitionTo(GameStatus::Completed)) {
    $game->update(['status' => GameStatus::Completed->value]);
}

Funciones útiles

  • "OrderStatus::cases()" → devuelve todos los casos.

    enum OrderStatus: string
    {
        case Pending   = 'pending';
        case Paid = 'paid';
        case Shipped   = 'shipped';
        case Delivered = 'delivered';
        case Cancelled = 'cancelled';
    }
    
    $all = OrderStatus::cases();
    
    var_dump($all);
    
    //Salida
    
    array(5) {
      [0] => enum(OrderStatus::Pending)
      [1] => enum(OrderStatus::Paid)
      [2] => enum(OrderStatus::Shipped)
      [3] => enum(OrderStatus::Delivered)
      [4] => enum(OrderStatus::Cancelled)
    
  • "OrderStatus::from('paid')" → convierte string a Enum. Busca el valor (->value) y devuelve el caso correspondiente. Si el valor no existe, lanza una excepción ValueError. Ideal cuando si se está 100% seguro de que el valor es correcto.

    enum OrderStatus: string
    {
        case Pending   = 'pending';
        case Paid = 'paid';
        case Shipped   = 'shipped';
        case Delivered = 'delivered';
        case Cancelled = 'cancelled';
    }
    
    $status1 = OrderStatus::from('paid');   // Funciona
    $status2 = OrderStatus::from('shipped');     // Funciona
    
    var_dump($status1);
    var_dump($status2);
    
    //Salida
    
    enum(OrderStatus::Paid)
    enum(OrderStatus::Shipped)
    
    // Esto lanza error:
    $status3 = OrderStatus::from('invalid');     // ValueError  pq no existe.
    
    
  • "OrderStatus::tryFrom('invalid')" → null si no existe (sin excepción). Convierte string a Enum (seguro, sin error). Es la versión segura de from(). Si el valor existe → devuelve el Enum. Si no existe → devuelve null. Recomendado cuando el dato viene de usuario, API externa, formularios, etc.

    enum OrderStatus: string
    {
        case Pending   = 'pending';
        case Paid = 'paid';
        case Shipped   = 'shipped';
        case Delivered = 'delivered';
        case Cancelled = 'cancelled';
    }
    $status1 = OrderStatus::tryFrom('paid'); // OrderStatus::Paid
    $status2 = OrderStatus::tryFrom('invalid');   // null (no lanza error) pq no existe.
    $status3 = OrderStatus::tryFrom('shipped');   // OrderStatus::Shipped
    
    var_dump($status1);
    var_dump($status2);
    var_dump($status3);
    
    //Salida
    
    enum(OrderStatus::Paid)
    enum(null)
    enum(OrderStatus::Shipped)
    
    
    //Ejemplo práctico:
    
    $status = OrderStatus::tryFrom($request->input('status'));
    
    if ($status === null) {
        abort(400, 'Estado inválido');
    }
    
    
  • "->value" y "->name"

    enum OrderStatus: string
    {
        case Pending   = 'pending';
        case Paid = 'paid';
        case Shipped   = 'shipped';
        case Delivered = 'delivered';
        case Cancelled = 'cancelled';
    }
    
    $status = OrderStatus::Paid;
    
    echo $status->value;   // El valor real (string/int) -> 'paid'.
    echo $status->name;    // El nombre del caso ->'Paid'