Installation:
composer require laranail/enumerator
No manual service provider registration needed (auto-discovered).
Define an Enum:
Create a class extending Laranail\Enumerator\Enum:
use Laranail\Enumerator\Enum;
enum UserRole: string
{
use HasEnumerator;
case ADMIN = 'admin';
case EDITOR = 'editor';
case VIEWER = 'viewer';
public function label(): string
{
return match($this) {
self::ADMIN => 'Administrator',
self::EDITOR => 'Content Editor',
self::VIEWER => 'Read-only User',
};
}
}
First Use Case:
Use the enum in a model with HasEnumerator:
use Laranail\Enumerator\HasEnumerator;
class User extends Model
{
use HasEnumerator;
protected $casts = [
'role' => UserRole::class,
];
}
Now User::first()->role returns a typed UserRole enum with built-in label access ($user->role->label()).
Declarative Attributes: Define metadata directly in the enum:
enum Status: string
{
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
public function attributes(): array
{
return [
self::PUBLISHED => [
'color' => 'green',
'icon' => 'check-circle',
],
self::ARCHIVED => [
'color' => 'gray',
'icon' => 'archive',
],
];
}
}
Access via $status->attributes['color'].
State Machines: Define transitions with guards:
enum OrderStatus: string
{
case PENDING = 'pending';
case PROCESSING = 'processing';
case SHIPPED = 'shipped';
case CANCELLED = 'cancelled';
public function transitions(): array
{
return [
self::PENDING => [
self::PROCESSING => fn($order) => $order->isPaid(),
],
self::PROCESSING => [
self::SHIPPED => fn() => true,
self::CANCELLED => fn() => true,
],
];
}
}
Use OrderStatus::transition($from, $to, $context) to validate transitions.
Bitmask Support: Combine flags:
enum Permissions: int
{
case READ = 1 << 0;
case WRITE = 1 << 1;
case DELETE = 1 << 2;
public function is(Permissions $mask): bool
{
return ($this->value & $mask->value) === $mask->value;
}
}
Usage: $user->permissions->is(Permissions::READ | Permissions::WRITE).
Validation: Use built-in rules:
use Laranail\Enumerator\Rules\EnumValue;
$request->validate([
'role' => ['required', new EnumValue(UserRole::class)],
]);
Eloquent:
Cast enums directly in $casts and use HasEnumerator for dynamic behavior.
Blade Components: Render enums with labels/icons:
@enum(UserRole::class, $user->role)
Customize via @enum('UserRole', $role, ['template' => 'custom-template']).
Filament/Livewire:
Use EnumField or EnumSelect components:
use Laranail\Enumerator\Filament\Fields\EnumSelect;
EnumSelect::make('role')
->enum(UserRole::class)
->required(),
Translations: Localize labels dynamically:
public function label(): string
{
return __("enums.user_role.{$this->value}");
}
Or use the HasTranslations trait for bulk localization.
Dynamic Enums: Store enum values in the database:
enum DynamicStatus: string
{
use HasDynamicEnumerator;
protected static string $table = 'dynamic_statuses';
}
Enum Value Conflicts:
Ensure enum values are unique. The package validates this during runtime but may throw EnumValueConflictException if duplicates exist.
Case Sensitivity:
Enum values are case-sensitive. Use strtolower() or strtoupper() in comparisons if needed:
if ($enum->value === strtolower($input)) { ... }
State Machine Circular References: Avoid infinite loops in transitions. Use explicit guards:
self::PROCESSING => [
self::SHIPPED => fn() => false, // Disallow self-transitions
]
Bitmask Overlaps: Ensure bitmask flags don’t overlap unintentionally. Test with:
assert((Permissions::READ->value & Permissions::WRITE->value) === 0);
Database Backed Enums:
For HasDynamicEnumerator, ensure the database table exists and has the correct schema:
php artisan enumerator:table dynamic_statuses
Enum Dump:
Use the dump() helper for enums:
\Laranail\Enumerator\Support\dump($enum);
Shows all metadata (labels, attributes, transitions).
Validation Errors: Check enum validation messages:
$validator->errors()->getMessages();
Customize messages in app/Providers/AppServiceProvider:
Validator::extend('enum_value', function ($attribute, $value, $parameters) {
return EnumValue::validate($value, $parameters[0]);
});
State Machine Debugging: Log transitions:
OrderStatus::transition($from, $to, $order)
->whenFailed(fn($errors) => \Log::error($errors));
Cache Transitions: Precompute transition rules if the enum is static:
public function transitions(): array
{
return cache()->remember('enum.transitions.OrderStatus', now()->addHours(1), fn() => [
// ...
]);
}
Batch Operations:
Use Enum::hydrate() for bulk creation:
$roles = UserRole::hydrate(['ADMIN', 'EDITOR']);
Avoid Reflection Overhead: For large enums, preload attributes:
$enum->loadAttributes();
Custom Attributes:
Extend HasEnumAttributes to add logic:
trait CustomAttributes
{
public function isActive(): bool
{
return $this->attributes['active'] ?? false;
}
}
Artisan Commands: Generate enums from migrations:
php artisan enumerator:make UserRole --values="ADMIN,EDITOR,VIEWER"
Rector Rules: Use Rector to migrate legacy string constants to enums:
vendor/bin/rector process src --dry-run
Testing:
Use EnumTestCase for assertions:
$this->assertEnumEquals(UserRole::ADMIN, $user->role);
$this->assertEnumTransitionAllowed(OrderStatus::PENDING, OrderStatus::PROCESSING, $order);
GraphQL/OpenAPI:
Enable modules in config/enumerator.php:
'modules' => [
'graphql' => true,
'openapi' => true,
],
How can I help you explore Laravel packages today?