tyghaykal/laravel-seed-generator
Laravel Seed Generator creates ready-to-run database seeders from your existing tables and data. Pick tables, export clean Laravel seeder classes, and skip hand-writing fixtures—ideal for demos, tests, and local resets.
The package is a dev-only convenience: it inspects your live database schema and writes ready-to-run Laravel seeder classes, either by snapshotting the rows that are already there or by generating fake rows that match each column's type. Treat it like make:model — a scaffolding step you run once and then edit by hand.
Install (dev only):
composer require --dev tyghaykal/laravel-seed-generator
Laravel's package auto-discovery should register the service provider; if you're on an older skeleton, add the provider to config/app.php manually. Confirm registration and get the exact command signature before anything else:
php artisan list | grep -i seed
php artisan help generate:seed # or whatever the registered command name is
That help output is the real source of truth for flags — this package has moved fast between releases and the README occasionally lags the actual signature. Read php artisan help first, then the README, in that order.
First use case. Pick one small, boring lookup table — roles, countries, categories. Don't start with users.
php artisan generate:seed roles
Then open the generated file in database/seeders/. You're evaluating three things at once:
DB::table('roles')->insert([...]) or does it hydrate models?created_at / updated_at / nullable columns?Those three answers determine almost everything about how you'll use it in practice. If a config/seed-generator.php exists, publish it now (php artisan vendor:publish --provider="...SeedGeneratorServiceProvider") so you have a stable set of defaults checked into the repo rather than relying on package defaults that can shift between minor versions.
Reference data as a baseline seeder. The highest-value, lowest-risk pattern: generate seeders for static lookup tables, commit them, and wire them into DatabaseSeeder::run() in dependency order. These tables rarely change, so a generated seeder is effectively a permanent fixture file. Regenerating is cheap and diffing the output catches accidental data drift.
Staging snapshot → scrubbed fixtures. The more powerful workflow, and the one that justifies the package's existence:
config/database.php) at a sanitized staging or read-replica DB.fake() calls or fixed placeholders.Keep step 4 aggressive. A users table with 200k rows produces an unusable multi-megabyte PHP file and a db:seed that takes minutes.
Test fixtures without factories. Generated seeders are a fast path to realistic test data for integration tests that care about shape rather than specific values — reporting queries, search, pagination, exports. The trade-off versus factories: seeders are static (deterministic, no per-test variation) and slow to insert in bulk. Use seeders for a small shared baseline (setUpBeforeClass / a trait that seeds once), factories for anything that needs per-test variety.
Connection and table targeting. Use the package's connection/table flags to scope generation narrowly rather than dumping the whole schema and deleting 80% of the output. Generating one table at a time also keeps you out of foreign-key-ordering trouble and makes the resulting diffs reviewable.
Treat output as a starting point, not an artifact. The genuinely professional pattern is: generate → immediately refactor. Move hard-coded values into a protected array $rows property, swap literal timestamps for now(), replace password hashes with Hash::make('password'), and extract anything reusable into a trait. If you find yourself regenerating the same table every sprint, that's a signal you should have hand-written the seeder the first time.
Keep it out of production paths. Composer --dev, never invoked from application code, never in a deploy script. The only production-adjacent use is generating a one-off seed for a new lookup table, which you then commit.
PII leakage is the headline risk. A generated seeder is a literal dump of your database committed to git forever. Run the generator against a sanitized source, or scrub before the first commit. Once a real email or token lands in git history, "we deleted the file" is not remediation. This alone is why the staging-snapshot workflow needs a scrub checklist, not just good intentions.
Model events don't fire. Generated seeders almost always use the query builder (DB::table(...)->insert()), which bypasses Eloquent entirely: no observers, no booted() hooks, no mutators, no casts, no HasUuids trait. If your app depends on a created observer to populate a slug, a search index row, or a tenant ID, the seeded data will be structurally invalid and you'll debug it downstream. Either post-process in the seeder or convert to model-based inserts.
Foreign keys and ordering. Per-table seeders are inserted in whatever order DatabaseSeeder::run() lists them. Parents before children, always. If you can't guarantee order, wrap in Schema::disableForeignKeyConstraints() / enableForeignKeyConstraints() — but know that this leaves orphaned rows that your app will choke on later. Ordering is the better fix.
Don't preserve auto-increment IDs blindly. Snapshotted IDs preserve intra-table relationships but will collide with anything else that assumes id = 1 is the first admin. If downstream code hard-codes IDs, that's a bug the seeder will happily mask. For lookup tables, dropping the id column and letting the DB assign is usually safer.
Passwords copy verbatim. Generated users seeders frequently contain real bcrypt hashes. They technically work, but you've now shipped credential material. Replace with a single known hash for a documented dev password.
Large tables blow up memory and file size. Cap rows. If the package streams or chunks, great; if not, generate with a LIMITed view or a WHERE clause and accept partial data. Also watch PHP's memory_limit on the generator itself, not just on db:seed.
Timestamps, JSON, binary, enums. created_at often comes out as a frozen literal date, which makes seeded data look stale in UI sorting tests. JSON columns may be emitted as escaped strings that don't round-trip through a cast. Binary/BLOB columns may be silently dropped or mangled. Regenerate, then eyeball one row of each awkward column type against select * from ... before trusting the output.
Regeneration drift. Nothing warns you when the schema changes and the committed seeder is now stale (new non-nullable column → seed fails at runtime). Add a cheap guard: a test that runs migrate:fresh --seed in CI. That catches every stale seeder in one shot.
Overwrite protection. Most generators refuse to clobber an existing file without a --force-style flag. Good — but it also means your edits silently persist while you think you regenerated. Read the command output; don't assume.
Maintenance risk. Single maintainer, ~83 stars, no dependents, and a release date in the future relative to typical usage — treat it as a convenience you can drop at any moment. Pin an exact version in composer.json, keep the generated seeders (they're yours, they don't need the package), and know that removing the package later costs you nothing but the regeneration step. Nothing in your application should depend on its runtime.
Debugging a bad seeder. Run it in isolation with php artisan db:seed --class=RolesSeeder against a scratch database — migrate:fresh first, so you're not debugging around leftover data. When insert order or a constraint is ambiguous, DB::listen() or DB::enableQueryLog() in a tinker session shows exactly what the seeder emits and where it dies.
Extension points. If the package supports publishing stubs/views, do it — a custom stub that emits now() for timestamps and fake() for common PII columns turns every future generation into something safe by default. If it doesn't, wrap the artisan call in your own small command that generates then post-processes, or just maintain a hand-edited database/seeders/base/ directory that you copy from. Either way, encode your team's conventions once rather than re-scrubbing every generation.
How can I help you explore Laravel packages today?