datomatic/laravel-enum-helper
Laravel Enum Helper adds handy methods to PHP enums: flag support, attribute casting, validation rules, labels, translations, select arrays, and comparison helpers, making it easy to use backed enums cleanly across your Laravel models, forms, and requests.
Install with Composer and let Laravel auto-discover the service provider:
composer require datomatic/laravel-enum-helper
There is no config file to publish — the package is a set of traits plus one validation rule and a generator command. The first place to look is the trait list in the README; start with HasEnumHelpers (a composite) if you want everything, or pick granular traits (EnumToArray, EnumToLabel, EnumToValues, EnumToApi, EnumToColor, EnumToIcon) to keep the enum surface small.
Generate your first enum:
php artisan make:enum Status --string
Then add the trait(s) and override label():
namespace App\Enums;
use Datomatic\EnumHelper\Traits\EnumToArray;
use Datomatic\EnumHelper\Traits\EnumToLabel;
use Datomatic\EnumHelper\Traits\EnumToValues;
enum Status: string
{
use EnumToArray, EnumToLabel, EnumToValues;
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
public function label(): string
{
return match ($this) {
self::DRAFT => __('Draft'),
self::PUBLISHED => __('Published'),
self::ARCHIVED => __('Archived'),
};
}
}
The first real use case is the one you repeat most: a <select> in a Blade form and the matching FormRequest validation.
// Controller
return view('posts.form', ['statuses' => Status::toArrayWithLabels()]);
<select name="status">
@foreach ($statuses as $value => $label)
<option value="{{ $value }}" @selected(old('status') === $value)>{{ $label }}</option>
@endforeach
</select>
// FormRequest
public function rules(): array
{
return [
'status' => ['required', new \Datomatic\EnumHelper\Rules\EnumRule(Status::class)],
];
}
Options arrays instead of array_map everywhere. Use values() for a flat list of backing values, names() for case names, toArray() when you need the enum shaped as an array, and toArrayWithLabels() when the array is going straight into a select. This removes the scattered array_map(fn ($c) => $c->value, Foo::cases()) calls that drift when a case is renamed.
Validation with EnumRule. It accepts an enum instance, a backing value, or (for array fields) a list of them, so you can stop hand-rolling Rule::in(Foo::values()) and keep the error message enum-specific.
'status' => ['required', new EnumRule(Status::class)],
'statuses' => ['array', new EnumRule(Status::class)], // or 'statuses.*'
The rule also lets you narrow the accepted cases (e.g. ->only([...]) / ->except([...])), which is handy for "can't move straight from Draft to Archived" style business rules — although for complex transitions, prefer a real state machine and keep the rule for input filtering.
API resources. toApi() returns a ['value' => ..., 'label' => ...] pair, which is the shape most frontends want for filters and badges. Return it from a resource or a small "options" endpoint:
public function toArray($request): array
{
return [
'status' => $this->status->toApi(),
'status_options' => Status::toApi(), // all cases, if needed
];
}
Model casts and queries. The enum is still a normal backed enum, so casts behave as usual:
protected $casts = ['status' => Status::class];
Post::whereIn('status', Status::values())->get();
Post::where('status', Status::PUBLISHED)->get(); // works on Laravel 9+
Filament / admin panels. EnumToColor and EnumToIcon implement Filament's HasColor / HasIcon contracts, so a single enum powers the table badge, the form select, and the filter in one place:
public function color(): string
{
return match ($this) {
self::DRAFT => 'gray',
self::PUBLISHED => 'success',
self::ARCHIVED => 'danger',
};
}
Localization. Because label() is a method evaluated at runtime, __() works normally and App::setLocale() in tests changes labels without any cache to clear.
Testing. Assert against the enum rather than strings:
$this->postJson('/posts', ['status' => Status::DRAFT->value])->assertCreated();
$this->assertSame('Draft', Status::DRAFT->label());
fromValue() / tryFromValue() only make sense on backed enums. On pure enums use fromName() / tryFromName(). Decide early; converting a pure enum to a backed one later is a breaking change for stored data.from* throws, tryFrom* returns null. This is consistent with native enum behavior, but it's the most common source of 500s when a request contains a stale value. Wrap in tryFromValue() when the input is user-controlled.label(), the trait derives something from the case name (headline-style). That's fine for English prototypes and wrong the moment a designer names the states differently — override label() as soon as UI copy exists.toArrayWithLabels() keyed by int backing values produces non-sequential keys, which json_encode renders as an object rather than an array. If a JS client expects an array, wrap with collect(...)->map(...)->values() or use the toApi() shape.HasEnumHelpers pulls in color and icon helpers; if you're not using Filament, that's dead surface area (and forces you to think about color() for no reason). Compose only the traits you use.Rule::enum. Laravel 9+ ships Rule::enum(). Pick one approach per project — the package rule is worth it when you also want labels and only/except, but mixing both in one FormRequest makes error messages inconsistent.toSelect() and friends are opinionated. Their exact output shape is aimed at a particular frontend convention; verify it against your component library before adopting it wholesale, and fall back to toArrayWithLabels() if the shape doesn't match.openCases(), default()) directly on the enum next to the traits; for grouping several enums (e.g. all status enums for a filter panel), use the package's EnumCollection rather than a random service class.EnumRule signature before copying examples from the README.How can I help you explore Laravel packages today?