suleymanozev/enum-field
Enum Field for Filament provides a simple Laravel form component that turns PHP enums into selectable options, with support for labels, colors, and validation so enum-backed values are easy to manage in Filament admin panels.
Install the field alongside Nova (it assumes laravel/nova is already required in your project):
composer require suleymanozev/enum-field
Most custom Nova fields ship a compiled JS bundle, but if resources/js is present in the vendor directory you'll need to publish and rebuild assets:
php artisan vendor:publish --tag=nova-assets # if the package registers a tag
php artisan nova:publish
Where to look first, in order:
README.md — check the exact make() signature and whether it expects an enum class, an array, or a closure.src/EnumField.php (or similarly named class) — confirm the component string and the public setters it exposes (->options(), ->enum(), ->labels(), etc.).resources/js/ — verify whether the field is a thin wrapper over Nova's built-in Select or a custom component; this determines which Nova props (displayUsingLabels, searchable, rules) will actually work.First use case — a model with a backed enum cast:
// app/Enums/Status.php
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
}
// app/Models/Post.php
protected $casts = ['status' => Status::class];
// app/Nova/Post.php
use Suleymanozev\EnumField\EnumField;
public function fields(Request $request)
{
return [
EnumField::make('Status')
->options(Status::class),
];
}
If that renders a select on the form and a readable label on index/detail, you've validated the package end-to-end. If the constructor signature differs, the src/ file is the source of truth — with a package this small, reading the 50–100 lines is faster than searching for docs.
Given the package has no dependents and effectively no community footprint, treat "getting started" as a 15-minute source audit: confirm the Nova version it targets (composer.json → laravel/nova constraint), confirm the Vue component is compiled, and confirm it degrades gracefully on Nova 4 vs Nova 5 before wiring it into more than one resource.
Centralize option lists on the enum itself. Rather than duplicating label arrays in every resource, give the enum a label() method or static options() and let the field consume it. This keeps filters, actions, and validators in sync:
enum Status: string
{
case Draft = 'draft';
case Published = 'published';
public function label(): string
{
return match ($this) {
self::Draft => 'Draft',
self::Published => 'Published',
};
}
public static function options(): array
{
return collect(self::cases())
->mapWithKeys(fn ($c) => [$c->value => $c->label()])
->all();
}
}
Then EnumField::make('Status')->options(Status::options()) (or ->options(Status::class) if the field accepts the class and calls cases() itself).
Consistent index vs. form behavior. The common split is: on forms you want a select of raw values; on index/detail you want the human label. If the field doesn't do this automatically, use Nova's displayUsing / resolveUsing hooks:
EnumField::make('Status')
->options(Status::class)
->displayUsing(fn ($value) => $value instanceof Status ? $value->label() : $value)
->onlyOnForms(),
Text::make('Status', 'status')
->displayUsing(fn ($value) => $value?->label())
->exceptOnForms(),
Or, if the field handles both, skip the duplication and verify in the browser.
Pair the field with Nova filters. Enums are a natural fit for a dropdown filter. Define the filter options from the same Status::options() source so a new case never has to be added twice:
public function options(Request $request)
{
return Status::options();
}
Use it inside actions and modals. Because Nova fields are reused across resources/actions, the field is handy for a "Change status" action's fields() array — same enum, same rendering, no extra Vue work.
Validation: always add explicit rules; don't rely on the field to constrain input server-side:
EnumField::make('Status')
->options(Status::class)
->rules(['required', Rule::enum(Status::class)])
Reuse for other enums. If the package accepts a class string, one EnumField covers every backed enum in the app — user roles, order states, visibility flags. Build a tiny app/Nova/Fields/StatusField.php wrapper if you find yourself passing the same options()/displayUsing() chain everywhere.
src/ and confirm the composer.json Nova constraint matches your installed Nova major. Pin the version in composer.json and consider vendoring/forking the single field class into app/Nova/Fields/ if it's under ~100 lines — you'll own a file instead of an abandoned dependency.public/vendor/nova / run php artisan nova:publish, then hard-refresh. Custom fields do not hot-reload.protected $casts = ['status' => Status::class], Nova receives a raw string and any ->displayUsing(fn (Status $s) => ...) type hint will throw. Guard with ?-> and a union check when values may be legacy/null.?Status and a null DB value, ensure the select has an empty option and that ->nullable() (Nova) plus 'nullable' in the rules are both set — otherwise saving an untouched form writes '' and fails the DB enum/column constraint.->value. If the field's options() maps label => value in the opposite direction from what you expect, the select will save labels and silently corrupt data. Verify by saving and inspecting the row.sortable() on an enum column sorts alphabetically by raw value, which often looks wrong. Either accept raw-value ordering or add a computed/orderByRaw sort.FormField vs Field base classes, and resolveUsing semantics changed. If the package targets one major, test the other explicitly rather than assuming.->help() and Log::info($field->jsonSerialize()) in the resource's fields() will show you exactly which meta (options, component name) the field sends to Vue. If meta is empty server-side, the bug is in the PHP class; if meta is populated but the UI is blank, it's the JS asset.Select::make('Status')->options(Status::options()) already covers 90% of use cases with no third-party risk. Reserve the package for the cases where it genuinely adds a label/display layer you don't want to reimplement.How can I help you explore Laravel packages today?