04-09-2022

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'