solution-forest/filament-scaffold
Filament Scaffold generates complete CRUD resources for Filament panels from your database schema, producing models, migrations, and resource files with forms, tables, and relations ready to use. Scaffold new resources fast and keep admin panels consistent while still customizing every generated fil...
solution-forest/filament-scaffold is a code-generation helper for Filament panels: you point it at a database table (or a model) and it emits the boilerplate — Resource class, form schema, table definition, and often the matching page/RelationManager stubs — so you spend your time on domain logic instead of TextInput::make('name')->required().
First steps, in order:
Read the README and the src/Commands directory before installing. This package is archived (last release 2025-07-28) and has zero dependents, so the code is the documentation. The stubs live under resources/stubs (or src/Stubs) — read those first; they tell you exactly what you'll get far faster than the README does.
Check the Filament constraint in its composer.json against the Filament version in your app. A scaffolding package is tightly coupled to Filament's schema API, and that API changed materially between v3 (->schema([...]), Forms\Components\*) and v4 (->form(Schema::make()...), Filament\Schemas\*). If your panel is on a newer major than the package targets, stop here and fork rather than fight it.
Install and publish:
composer require solutionforest/filament-scaffold
php artisan vendor:publish --tag="filament-scaffold-config"
php artisan vendor:publish --tag="filament-scaffold-stubs" # if available — check tags
php artisan list | grep -i scaffold
The last command matters: confirm the actual command signature in your installed version rather than trusting a README written for an older tag.
If it registers as a Filament plugin, add it to your PanelProvider:
->plugins([
\SolutionForest\FilamentScaffold\FilamentScaffoldPlugin::make(),
])
If it's command-only, skip this — many scaffolders are pure artisan tools with no panel surface.
First use case: pick a table you already understand well (posts, categories), generate the resource, and read the output line by line before committing. That single exercise tells you more about the package's opinions — column-type mapping, relationship detection, naming — than any amount of docs.
Table-first generation (the main workflow). Scaffolders in this family read Schema::getColumns() / getForeignKeys() and map each column to a Filament component. The day-to-day loop is: change a migration → run the generator → diff the output → keep what you want. Treat generated files as a first draft commit, not as owned code.
Config-driven type mapping. The published config is the real extension surface. Typical shape:
return [
'type_map' => [
'string' => \Filament\Forms\Components\TextInput::make(...),
'text' => \Filament\Forms\Components\Textarea::make(...),
'boolean' => \Filament\Forms\Components\Toggle::make(...),
'datetime' => \Filament\Forms\Components\DateTimePicker::make(...),
],
'ignored_columns' => ['id', 'created_at', 'updated_at', 'deleted_at'],
'namespace' => 'App\\Filament\\Resources',
'path' => app_path('Filament/Resources'),
];
Fork the config map to your house style (every string gets ->maxLength(255), every money column gets ->numeric()->prefix('$')) and every future generation is already half-reviewed.
Relationship handling. The useful pattern is: belongsTo → Select::make('author_id')->relationship('author', 'name'); belongsToMany → ->multiple(); hasMany/belongsToMany → a RelationManager stub. Verify each generated relationship by opening the resource and running the page — relationship detection is the single most common place these generators get it wrong (polymorphic relations, custom foreign key names, pivot tables).
Keep generated code disposable. Adopt one of two disciplines:
// @generated) so you know which files are safe to overwrite. This is the safer default for anything customer-facing.Team presets. Wrap the command in your own artisan command that passes your standard flags (namespace, panel, --generate-relation-managers, --no-migrations). New projects then get identical scaffolding with one command, and you have a single place to patch when you eventually fork the package.
Dev-environment seeding. A fast way to spin up a demo panel: point the generator at a scratch schema, generate, and ship it as a demo panel behind a feature flag. Keep it out of production routes.
Drift check in CI (optional). If you rely heavily on regeneration, add a CI job that runs the generator into a temp directory and fails on unexpected diffs. It catches schema changes that never made it into the UI.
"solutionforest/filament-scaffold": "x.y.z", not ^x.y) and vendor the source into your repo, or fork it. Budget a half-day to fork if you expect to keep using it past the next Filament upgrade.Method ...::schema does not exist or similar, you're on the wrong major — don't patch the stubs by hand across dozens of files, fork the stub directory once.composer update, and after php artisan config:cache you'll chase a ghost.json/jsonb, decimal with scale, unsignedBigInteger, spatial types and uuid usually degrade to a plain TextInput. Audit generated forms against SHOW CREATE TABLE for any table with those types.--no-migrations (or the equivalent) on existing databases.git status before every run is the cheapest safeguard; if the package supports a --force/--no-overwrite flag, leave it in the safe position by default.->softDeletable(), ->timestamps() filters, and — crucially — authorization. Pair with filament-shield (or your own policies) rather than assuming generated resources are access-controlled.-vvv, then open the stub file for the artifact you expected and compare it to the output. Nine times out of ten the mismatch is a stub the package didn't publish, a config key with a different name than the README claims, or a type the map doesn't know.php artisan config:clear && php artisan filament:optimize-clear (or optimize:clear). Stale config after editing the type map produces confusing "my change did nothing" moments.vendor/ at all costs.How can I help you explore Laravel packages today?