datomatic/nova-enum-field
A Laravel Nova field for working with PHP enums—display them as badges or labels, edit via select inputs, and filter Nova resources by enum values. Supports backed and pure enums, nullable values, and customizable colors for clean, type-safe admin panels.
Architecture fit. datomatic/nova-enum-field is a thin, Nova-native extension: a field class (Fields/Enum.php) that renders PHP 8.1 backed-enum cases into a select and persists the backing value, plus two filter classes (EnumFilter, EnumBooleanFilter) that translate enum cases into whereIn constraints. It adds no service provider surface, no persistence layer, no migrations — it decorates Nova's existing field/filter contract. Architecturally this is a presentation adapter over a domain type, which is the right shape: the enum on the model cast remains the source of truth, and the package only maps cases → labels → query constraints. Fit is contingent on two facts, not opinions: (1) we are on Laravel Nova (v4/v5), and (2) our state columns are backed enums cast on the model. If either is false, the package is inert.
Integration feasibility. Low effort, high determinism. Install is a single composer require; wiring is three lines per resource (field + rule + filter registration). Because the surface is a few hundred lines and readable in minutes, we can verify behavior by reading src/ and tests/ rather than trusting docs — a meaningful de-risker for a low-signal package. The one non-obvious integration constraint is that filters must be returned from Resource::filters() and the resource must be reachable via availableFilters(); a missed second step yields a silently absent filter, not an error. Second constraint: do not mix ->options() with ->enum() — enum() populates options internally and a later options() call overwrites them, producing a phantom mapping bug that costs more debugging time than the package saves.
Technical risk. Classify as a vendored utility, not a strategic dependency. Evidence: 19 stars, zero dependents, single maintainer, MIT. That is a bus-factor of one and no downstream validation signal. Offsetting evidence: v1.11.1 is a named-contributor fix to a real production crash (exception #8 when EnumFilter/EnumBooleanFilter is applied to an index rendered through a relation), which demonstrates active triage and, importantly, that someone is running this against nested Nova indexes. A patch release does not upgrade the risk class — it upgrades our confidence in the fork-and-own plan, which is cheap here because the code is small. The dominant residual risk is Nova major-version skew: the package must track Nova's internal field/meta contract, and Nova majors are where that contract moves. Treat every Nova upgrade as a breaking-change event for this package.
Key questions.
composer.json constraint actually admit it? Verify against the repo, not the README.Select with an options array is ~20 lines and this dependency is not justified.string/int) and cast on the model? Pure enums render but filters silently fail to narrow; int-backed enums on VARCHAR columns pass validation but produce filters that never match.^1.11.1 is mandatory.Stack fit. Requires PHP 8.1+ (native enums) and Laravel Nova 4/5. Both are gating, not aspirational. The package assumes Laravel's Rule::enum validation exists alongside it — it does not replace validation, so the resource rules stay ours. It composes with stock Nova field modifiers (hideWhenCreating, hideFromIndex, nullable, help) rather than introducing package-specific flags, which keeps resources readable and portable. It also emits backing values from filters, so the same EnumFilter instance works in resource filters and in metric/card queries with no translation layer — a small but real reduction in duplicated query logic across dashboards.
Migration path. Strangler, per column, low blast radius:
^1.11.1 in a branch.HasLabel + model cast), leaving the existing Select in place. The cast is the source of truth; the field follows it.Select::make(...)->options([...]) for Enum::make(...)->enum(...) on one resource. Add 'required'/'nullable' plus Rule::enum.EnumFilter for multi-case, EnumBooleanFilter for binary), register it in filters() and availableFilters().Order matters: enum + cast before field, field before filter. Reversing it produces filter comparisons against raw strings and looks like a package defect.
Compatibility. The hard boundary is the Nova major. Within a major, the package is stable because it touches a narrow contract. The known incompatibility classes are all data-shape issues rather than version issues: pure (non-backed) enums, int backing on string columns, nullable columns where only the field is nullable, and wrong filter class selection (EnumBooleanFilter vs EnumFilter — both render fine, one never narrows). Blank field/no styles is an asset-publish symptom, not a compatibility failure: compiled assets ship in dist/, but php artisan nova:publish plus a Nova cache clear is the first move before filing a bug.
Sequencing.
composer.json, read src/ and tests/, run a throwaway field against one enum in a dev environment.Explicit non-goal. This does nothing for customer-facing UX and does not justify Nova spend. Scope it as internal-tooling spend only.
Maintenance. We own it. Single maintainer, zero dependents, MIT — the correct posture is to vendor or fork the code in-repo, pin the version, and treat upstream as a convenience rather than a lifeline. The economics favor this: the surface is a few hundred lines, so forking is a realistic, low-drama plan rather than an emergency contingency. Budget roughly half a day per quarter for upgrade verification and near-zero for feature work, since there is no feature pressure — the package does one thing.
Support. The win here is operational self-service. Filterable enum columns let support and ops slice queues by state (order status, subscription lifecycle, ticket priority, KYC state) without filing engineering tickets, and since 1.11.1 that extends to relation-scoped views — the nested panels where support actually spends its day. This is the primary business justification: it converts recurring "can you pull a list of X in state Y" requests into self-service clicks. Secondary support benefit: replacing hand-maintained options arrays with enum-derived labels eliminates a class of drift bug where the select options and the database values disagree.
Scaling. Query behavior is standard whereIn on indexed, low-cardinality columns — the shape databases handle best. No N+1 concerns beyond what Nova index rendering already implies, and label resolution happens in PHP from an in-memory enum, so there is no per-row query cost. The scaling risk is not throughput, it is breadth: the more resources that depend on the package, the wider the blast radius of a Nova upgrade. Mitigate by keeping the dependency behind a small number of field/filter usages and by centralizing enum definitions so label changes are single-point.
Failure modes.
VARCHAR column. Renders correctly, never narrows. Highest-cost failure mode because it looks like a data problem.->options() called after ->enum() silently overwrites the generated options.availableFilters(); no error, just an absent UI control.nova:publish and cache clear, not a package bug.Ramp-up. Very low. One engineer can be productive in an afternoon: install, read src/ (the package is effectively self-documenting), model the enum with HasLabel, wire field + filter, verify via the field's meta JSON in Nova's debug output rather than reading the Vue component. Debugging guidance worth codifying for the team: (1) inspect meta first to confirm options/labels map correctly; (2) confirm HasLabel is implemented on the enum before suspecting the field, since Nova falls back to case names; (3) check the migration's column type before blaming the filter; (4) verify the filter appears in both registration points. Add exactly one regression test — an enum filter on a relation-mounted resource — as the standing guard for the failure class this package has already exhibited once.
How can I help you explore Laravel packages today?