Laravel cookbook · Beginner

Use backed enums and casts on models

Statuses are free-form strings with typos, and money or JSON columns need manual conversion.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Cast status columns to backed enums, validate with the Enum rule, and add behaviour (labels, allowed transitions) to the enum. Cast JSON and dates explicitly.

Solution

<?php
// app/Enums/OrderStatus.php
namespace App\Enums;

enum OrderStatus: string
{
    case Pending = 'pending';
    case Paid = 'paid';
    case Shipped = 'shipped';
    case Cancelled = 'cancelled';

    public function canTransitionTo(self $next): bool
    {
        return match ($this) {
            self::Pending => in_array($next, [self::Paid, self::Cancelled], true),
            self::Paid => in_array($next, [self::Shipped, self::Cancelled], true),
            self::Shipped, self::Cancelled => false,
        };
    }
}
<?php
// app/Models/Order.php
namespace App\Models;

use App\Enums\OrderStatus;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected function casts(): array
    {
        return [
            'status' => OrderStatus::class,
            'shipping_address' => AsArrayObject::class,
            'paid_at' => 'immutable_datetime',
        ];
    }

    public function transitionTo(OrderStatus $next): void
    {
        if (! $this->status->canTransitionTo($next)) {
            throw new \DomainException("Cannot move order from {$this->status->value} to {$next->value}");
        }
        $this->update(['status' => $next]);
    }
}

// Validation: ['status' => [Rule::enum(OrderStatus::class)]]

Pitfalls

  • Comparing enum-cast attributes to strings ($order->status === 'paid'), which is always false.
  • Database enum column types that require migrations for every new value; use strings plus casts.
  • Updating statuses directly without checking allowed transitions.

Variations

  • Store money as integer cents (or use a money library cast) rather than floats.
  • AsEnumCollection::of(Permission::class) casts JSON arrays of enum values.

Further reading: Enum casting

Reference: https://laravel.com/docs/13.x/eloquent-mutators#enum-casting

More Laravel recipes

Was this page helpful?