Installation:
composer require laudis/typed-enum
Add to composer.json under require:
"require": {
"laudis/typed-enum": "^1.0"
}
First Enum Class:
Create a final class extending TypedEnum in app/Enums/ (or your preferred namespace):
namespace App\Enums;
final class UserRole extends \TypedEnum
{
private const ADMIN = 'admin';
private const EDITOR = 'editor';
private const VIEWER = 'viewer';
}
First Usage:
use App\Enums\UserRole;
// Create an instance
$role = UserRole::ADMIN();
// Get the value
$roleValue = $role->getValue(); // 'admin'
// Strict comparison
if ($role === UserRole::ADMIN()) {
// Do something
}
IDE Integration:
Add @method tags to your enum class for autocompletion:
/**
* @method static UserRole ADMIN()
* @method static UserRole EDITOR()
* @method static UserRole VIEWER()
*/
final class UserRole extends \TypedEnum
{
// ...
}
OrderStatus, PaymentMethod).string, int, or float values.
final class OrderStatus extends \TypedEnum
{
private const PENDING = 'pending';
private const COMPLETED = 1;
private const CANCELED = 2.0;
}
private const (PHP 7.4+).public function updateStatus(Request $request, OrderStatus $status)
{
// $status is guaranteed to be a valid OrderStatus enum
}
public function findByStatus(OrderStatus $status): Collection
{
return Order::where('status', $status->value)->get();
}
protected $casts = [
'status' => OrderStatus::class,
];
$pendingOrders = Order::where('status', OrderStatus::PENDING()->value)->get();
$table->string('status')->comment('OrderStatus::PENDING, OrderStatus::COMPLETED, etc.');
public function toArray(): array
{
return [
'status' => $this->status->value,
'name' => $this->status->name(),
];
}
use Illuminate\Validation\Rule;
$rules = [
'status' => ['required', Rule::in(array_column(OrderStatus::cases(), 'value'))],
];
$statuses = OrderStatus::resolve('pending'); // Returns array of matching enums
$firstMatch = $statuses[0] ?? null;
$activeRoles = UserRole::resolve('admin')->merge(UserRole::resolve('editor'));
public function test_status_comparison()
{
$this->assertSame(OrderStatus::PENDING(), OrderStatus::resolve('pending')[0]);
}
it('validates order status', function () {
expect(OrderStatus::PENDING())->toBe(OrderStatus::resolve('pending')[0]);
});
/**
* @extends \TypedEnum<string>
*/
final class UserRole extends \TypedEnum
{
// ...
}
UserRole::INVALID).foreach (OrderStatus::cases() as $status) {
// $status is an instance of OrderStatus
}
$completedStatuses = collect(OrderStatus::cases())
->filter(fn ($status) => $status->value === 'completed');
// config/app.php
'default_role' => UserRole::VIEWER()->value,
config(['app.roles' => array_column(UserRole::cases(), 'value')]);
protected $signature = 'order:update {status : OrderStatus}';
public function handle()
{
if ($this->argument('status') === OrderStatus::CANCELED()) {
// Handle cancellation
}
}
public $status = OrderStatus::PENDING();
public function updatedStatus()
{
$this->validate(['status' => 'required|in:' . implode(',', OrderStatus::cases())]);
}
public function mount()
{
$this->statusOptions = collect(OrderStatus::cases())
->pluck('value', 'value')
->toArray();
}
public function handle(OrderStatus $status)
{
// $status is guaranteed to be valid
}
UpdateOrderStatus::dispatch(OrderStatus::COMPLETED());
public function update(User $user, Order $order)
{
return $user->role === UserRole::ADMIN() ||
$order->userId === $user->id;
}
class OrderStatusUpdated implements ShouldBroadcast
{
public function __construct(public OrderStatus $status) {}
}
public function toMail(Order $order)
{
return (new MailMessage)
->line("Order status updated to: {$order->status->value}");
}
public function saved(Order $order)
{
if ($order->status === OrderStatus::COMPLETED()) {
// Trigger completion logic
}
}
public function toArray($request)
{
return [
'status' => $this->resource->status->value,
'status_name' => $this->resource->status->name(),
];
}
Duplicate Values:
const A = 1; const B = 1;), resolve() returns an array.resolve():
$matches = OrderStatus::resolve('pending');
$status = $matches[0] ?? throw new \InvalidArgumentException('No matching status');
Case Sensitivity:
'Admin' ≠ 'admin').snake_case or UPPER_CASE).Private Constants:
private const. Use protected const as a fallback.protected const ADMIN = 'admin';
IDE Autocompletion:
@method tags, IDEs won’tHow can I help you explore Laravel packages today?