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.
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
- Authorize actions with policies
- Cache expensive data with stale-while-revalidate
- Call external APIs with timeouts, retries and fakes
- Catch N+1 queries with strict Eloquent mode
- Delete old records automatically with prunable models
- Dispatch events and listeners after the transaction commits
- Issue scoped API tokens with Sanctum
- Make queued jobs reliable with retries, backoff and uniqueness