krlove/code-generator
krlove/code-generator is a Laravel package that scaffolds PHP classes from your existing database schema, automating model and related boilerplate so you can skip repetitive hand-written code and keep generated classes consistent with your tables.
krlove/code-generator is a reverse-engineering tool: it reads your existing database schema and emits Eloquent model classes. It is not a migration or scaffolding package — the DB is the source of truth, and the models are the output.
Install and publish config:
composer require krlove/code-generator --dev
php artisan vendor:publish --provider="Krlove\CodeGenerator\CodeGeneratorServiceProvider"
Run the generator against your default connection:
php artisan code:models
First use case to try: point it at a table you already understand (users) and inspect the result before trusting the whole run.
php artisan code:models --table=users
You'll get a class in app/ (namespace App by default, per config/code-generator.php) extending Illuminate\Database\Eloquent\Model, with $table, $primaryKey, $timestamps, $fillable, @property docblocks derived from column types, and relation methods inferred from foreign keys.
Where to look first:
config/code-generator.php — the model_defaults block (namespace, base_class_name, connection, no_timestamps, date_format, backup). Set this once so every generation matches your conventions.php artisan code:models --help — options include --table, --connection, --namespace, --parent, --no-timestamps, --date-format, --backup, --filename, --path.Brownfield onboarding. The strongest day-to-day use is a legacy or externally-owned database. Generate everything once, then hand-curate. Do not treat generated output as final code — treat it as a first draft you commit and then edit.
Generate into a scratch directory, then move. Avoid letting the tool write directly into app/Models on a codebase with hand-edited models:
php artisan code:models --path=database/generated --namespace="App\Generated"
Review the diff, copy what you need, discard the rest.
Custom base class instead of repeating behavior. Point base_class_name (or --parent) at your own App\Models\BaseModel so generated models inherit UUID keys, global scopes, $guarded, cast conventions, etc. This is the single highest-leverage config change.
// config/code-generator.php
'model_defaults' => [
'namespace' => 'App\\Models',
'base_class_name' => App\\Models\\BaseModel::class,
'connection' => 'legacy',
'backup' => true,
],
Per-table regeneration during schema drift. When a table changes, regenerate just that table rather than the whole schema:
php artisan code:models --table=orders --backup
--backup writes a .backup sibling; diffing it against the overwritten file shows exactly what the schema change did. Add *.backup to .gitignore unless you deliberately keep them.
Multi-connection apps. Pass --connection= for each database rather than relying on the default. Generate per-connection into separate namespaces (App\Models\Billing, App\Models\Reporting) so ownership is obvious.
CI drift check. Because output is deterministic, you can assert models match the schema:
php artisan schema:dump --prune # fast local DB
php artisan code:models --path=/tmp/gen
diff -r /tmp/gen app/Models || echo "models out of sync"
This is useful when the DB is owned by another team and you want a warning when a deploy ships a column you haven't modeled.
Read-only legacy models. For reporting-side tables you never write to, generate normally, then strip $fillable and set $guarded = ['*'] in the model or base class. The generator's $fillable is literal — every column ends up mass-assignable.
It is archived (last release 2022-02-20). No Laravel 10/11 guarantees. Its doctrine/dbal constraint is the usual failure point — DBAL 3/4 removal of getSchemaManager() and type-mapping changes break schema introspection. Pin your Laravel/DBAL versions, or fork the package and patch the DBAL calls. On a modern app, budget a day for the fork; don't assume composer require just works.
Overwrites are silent. Running code:models without --table or --path rewrites every model in place. If you have added accessors, scopes, or relationships by hand, they are gone. --backup mitigates but does not prevent. Always scope to a table or a scratch path until you trust the workflow.
Relations only exist if foreign keys exist. No FK constraint → no belongsTo/hasMany. In MySQL be aware of engine/FOREIGN_KEY_CHECKS history; in SQLite FKs are often declared but off. Inspect generated relation methods against information_schema before assuming completeness.
Pivot detection is heuristic. Many-to-many is inferred from table naming and column shape. It will occasionally emit a belongsToMany where you wanted two hasManys, or pick the wrong related model on ambiguous names. Fix by hand and re-generate only per-table from then on.
Timestamps are name-based. --no-timestamps is needed for tables without created_at/updated_at, and nonstandard names (e.g. created/modified) require --no-timestamps plus manual const CREATED_AT. --date-format only helps if your column format is uniform.
Nullable and default values can be lossy. Generated $fillable doesn't encode nullability; @property docblocks reflect column type but not ?. If you rely on static analysis (PHPStan/Larastan), review the docblocks or regenerate with a tightened base class.
Enum and JSON columns. Depending on DBAL version, native MySQL ENUM may surface as string or throw during type mapping; JSON may not get a $casts entry. Add casts manually and expect to lose them on the next regeneration of that table.
Large schemas. Generate per-connection and per-table for big databases; a single full run introspects everything in memory and is slow against remote DBs. Run it against a schema:dump-loaded local copy when possible.
Extension points are thin. There are no custom stubs or post-generation hooks. The practical extension pattern is: custom base model via config, plus your own thin Artisan command that shells out to code:models --path=... and then applies a post-processing pass (adding traits, rewriting $fillable, running php-cs-fixer). If you need more than that, vendor the package and edit the model template directly.
How can I help you explore Laravel packages today?