markbaker/enumhelper
A lightweight PHP helper for native enums, offering convenient methods for listing cases, names and values and converting enums into arrays or options for forms, validation and select inputs.
composer require markbaker/enumhelper
No service provider, no config file, no publish step — it's pure trait glue. Requires PHP 8.1+ (enums themselves), so confirm your composer.json platform constraint before adding it.
Start by reading src/Traits/ in the repo. That directory is the package; there are no hidden features. Expect two flavours: a trait for backed enums (raw values, name→value maps) and a trait for pure/unit enums (case names). The method names are the whole API surface, so skim the trait bodies once rather than relying on memory — the docs are thin and the package hasn't been touched since early 2022.
First real use case: stop writing array_column(Status::cases(), 'value') in every FormRequest, migration, and factory.
enum Status: string
{
use \MarkBaker\EnumHelper\Traits\BackedEnumTrait;
case Draft = 'draft';
case Published = 'published';
case Archived = 'archived';
}
Then Status::values() / Status::toArray() are available everywhere you need a plain array.
Validation rules. The highest-value pattern — it kills the most duplicated code:
$request->validate([
'status' => ['required', Rule::in(Status::values())],
]);
Blade select options. toAssoc() (where available) gives you ['Draft' => 'draft', ...] for <option> generation; otherwise combine names() and values() with array_combine(). Push toArray() through json_encode() for a JS-side lookup rather than duplicating the enum in TypeScript.
Hydrating from loose strings. When a value arrives from a legacy table, a config file, or an external API, fromName() / tryFromName() let you resolve by case name without a match block. Prefer tryFromName() in request/csv parsing paths and handle null explicitly; reserve the throwing variant for seeders and console commands.
Convention over inheritance. Enums can't extend each other, so you can't centralise the trait. Pick one: either add the same trait set to every enum by convention, or define an architecture test that reflects over app/Enums and asserts each class uses the expected trait. The latter is cheap and pays for itself the first time someone adds an enum and forgets.
Coexists with Laravel casts. These helpers are for outside the model (arrays, rules, JSON); the 'status' => Status::class cast still handles attribute hydration and $model->status. Don't reach for the trait inside accessors — use $this->status->value there.
Wrap for collections. The helpers return plain arrays. If your codebase leans on fluent collection work, add a tiny local macro or a collect(Status::toArray()) at the call site rather than expecting a Collection back.
cases().use another enum helper package (or hand-roll a values()), PHP fatals on the trait conflict only if both come from traits — a method defined directly on the enum simply wins. Audit for duplicate helper methods before layering packages.toArray() semantics differ by trait. On a backed enum it means values; on a pure enum it means names. Never assume the two traits are interchangeable in a generic service.Rule::in(Status::values()) compares against ints; incoming form data is strings. Laravel's integer/numeric casting rules or an explicit (int) cast prevent frustrating in failures.App\Support\Enums and dropping the dependency is a defensible call — you lose nothing but the composer require.static::cases(). Dump that first — the bug is nearly always a case you forgot to add, not the trait.App\Support\Concerns\HasLabels alongside it for label()/color()/description() and compose both into each enum. Keep vendor traits untouched so upgrades (should one ever appear) stay clean.How can I help you explore Laravel packages today?