dobron/laravel-db-enum-generator
Laravel package that generates PHP enum classes from database column values, keeping enum cases in sync with stored data. Useful for replacing magic strings with type-safe enums in models, validation rules, and forms.
This is a code-generation utility, not a runtime dependency. Install it as a dev dependency so it never ships to production:
composer require --dev dobron/laravel-db-enum-generator
Then locate the actual surface area before writing any code:
php artisan list | grep -i enum (or php artisan list dobron) to get the exact signature — READMEs for small packages like this drift, and at 3 stars / a 2023-09-04 release you should trust php artisan help <command> over the docs.php artisan vendor:publish --provider="Dobron\LaravelDbEnumGenerator\...ServiceProvider" (confirm the provider with php artisan about or composer show dobron/laravel-db-enum-generator). The config is where you map database sources → target enum classes, namespaces, and output paths.app/Enums so generated files sit next to hand-written enums and are easy to git diff in review.First use case: the classic statuses / types lookup table. You have a order_statuses table (id, name) that front-end dropdowns and API validation both depend on. Rather than duplicating those values in three places, generate a backed enum:
enum OrderStatus: string {
case Pending = 'pending';
case Shipped = 'shipped';
case Cancelled = 'cancelled';
}
Configure that table → class mapping, run the generator once, and commit the output. That single generated file becomes the contract between the DB, your models, and your validation layer.
Eloquent casts are the highest-leverage integration. Once an enum exists, cast the column on the model:
protected $casts = [
'status' => OrderStatus::class,
];
// $order->status === OrderStatus::Shipped
// Order::where('status', OrderStatus::Shipped) // still works via backed value
This is where the generator pays for itself — you get typed property access without hand-maintaining the case list.
Validation rules. With Laravel 9+, Rule::enum(OrderStatus::class) in a FormRequest means the generated enum is the single source of truth for "what statuses are legal." Adding a row to the lookup table and regenerating propagates to validation automatically.
Seeders and factories. Use OrderStatus::cases() to seed lookup tables from the enum, or the reverse — seed the table and let the generator read it. Pick one direction; circular regeneration (enum → seed → enum) is a source of confusing drift.
CI drift check. Because generation is deterministic, add a step that runs the generator and fails if git diff --exit-code shows changes. This catches the real-world failure mode: someone adds a status via SQL in staging, the code works there, and production 500s because the enum never got the case.
Regeneration cadence. Don't run it on every request or in a deploy hook blindly — it's a developer-time tool. Run it after migrations that seed reference data, review the diff, commit. Treat the output as source, not build artifact, unless you're explicitly generating into a gitignored directory and rebuilding on deploy.
Keep generated enums dumb. Add helper methods (label(), color(), isTerminal()) via a trait applied in a config-provided base, or wrap the enum in a small value object. Anything you hand-edit inside the generated file disappears on the next run.
Health signal. 3 stars, 0 dependents, last release September 2023. It's a legitimate time-saver but it is not battle-tested by a large community. Budget an hour to read the source before adopting it in anything customer-facing, and pin the version in composer.json. If it breaks on a future Laravel major, you may be maintaining a fork — which is acceptable for a ~200-line generator, but plan for it.
Enum column vs. lookup table are different problems. MySQL ENUM('a','b') columns can be read from information_schema; Postgres uses native enum types with their own catalog. A lookup-table approach works identically on both. Confirm which mode the package supports — if it only reads MySQL information_schema, you'll get silent empty output on Postgres rather than an error.
Value/case sanitization. DB values like in-progress, 2, or N/A don't map cleanly to PHP case names. Expect the generator to apply some transformation (camelCase, strip non-alphanumerics). Verify the generated ->value still matches the DB exactly — a mangled value breaks where() queries silently, since the cast and the stored string won't line up.
Backed type mismatches. Postgres integer status codes will generate enum Foo: int, string slugs enum Foo: string. If the column is nullable, the cast handles null fine, but validation and Rule::enum do not implicitly allow null — add 'nullable' explicitly.
Connection config. If your enum source lives on a read replica or a non-default connection, set it in config. Generation against the wrong connection is the most common "it generated an empty enum" cause — check config('database.default') and the package config together.
Debugging empty or partial output. The failure mode is quiet: missing tables produce no cases rather than an exception. Run with -v, diff the generated file against the DB (SELECT DISTINCT status FROM ... ORDER BY 1), and confirm the table/column names in config match reality — snake_case vs. pluralization assumptions bite here.
Extension point. If the package exposes a configurable stub/template, that's your hook for adding a @generated docblock, a label() method stub, or declare(strict_types=1). If it doesn't, keep the generated enum pristine and put behavior in a trait — this survives package upgrades and fork-ability.
The honest cost/benefit. For a stable 5-value status column, hand-writing the enum is faster than configuring and debuging a generator. This package earns its place when you have many lookup tables, frequently changing reference data, or a legacy DB that's the actual source of truth. Below that threshold, the Laravel-native approach — write the enum by hand, seed the DB from it — is less machinery for the same result.
How can I help you explore Laravel packages today?