thetheago / laravel-seeder-versioning
A Laravel package that provides version control for database seeders, similar to Laravel's migration system. This package tracks which seeders have been run and only executes seeders that have been modified by monitoring class content changes.
Package info
github.com/thetheago/laravel-seeder-versioning
pkg:composer/thetheago/laravel-seeder-versioning
Requires
- php: >=8.3
- illuminate/support: ^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^10.0
- phpunit/phpunit: ^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-01 01:04:08 UTC
README
A Laravel package that provides version control for database seeders, similar to Laravel's migration system. This package tracks which seeders have been run and only executes seeders that have been modified by monitoring class content changes.
Features
- Tracks seeder execution history
- Only runs modified seeders
- Validates seeder content changes using hash verification
- Simple integration with Laravel's existing seeder system
- Option to generate hash versioning without running seeders
Installation / Configuration
You can install the package via composer:
composer require thetheago/laravel-seeder-versioning
Publish the package configuration and migrations:
php artisan vendor:publish --tag=seeder-versioning
Run the migrations to create the seeder versioning table:
php artisan migrate
Usage
Running Seeders
To run your seeders with version control:
php artisan seed:migrate
This command will:
- Check all your seeders
- Compare their content against previously run versions
- Only execute seeders that have been modified or haven't been run before
Before running, it shows what will run and asks for confirmation:
+---------------+----------+--------+----------------------------------+
| Seeder | Status | Hash | Risks |
+---------------+----------+--------+----------------------------------+
| RoleSeeder | modified | 49ab5e | [high] truncate() on table roles |
| ProductSeeder | new | cd5947 | [medium] insert() without upsert |
+---------------+----------+--------+----------------------------------+
2 seeders, 1 high risk will be run. Do you want to continue? (yes/no) [no]:
- Risks: found by static analysis of the current code of each seeder that will run (
token_get_all(), nothing is executed and nothing is stored). It is a heuristic, not a guarantee:
| Pattern | Risk | Level |
|---|---|---|
truncate(), delete(), forceDelete(), DB::statement('DROP/TRUNCATE/DELETE ...') |
Deletes existing data | high |
insert() / create() without upsert/updateOrCreate/firstOrCreate/updateOrInsert/insertOrIgnore in the seeder |
May duplicate data on rerun | medium |
$this->call(...) |
Runs other seeders too | medium |
factory() / ::factory() |
Random data, not idempotent | medium |
Schema::disableForeignKeyConstraints() |
Disables foreign keys | medium |
When is the question asked:
- In a terminal, always. Answering
noruns nothing and exits with1. - Without a terminal (
--no-interaction, CI), it runs without asking, except in production (APP_ENV=production), where it is cancelled, likedb:seed. --forceskips the question everywhere (deploys).
To only see the table, without running anything or creating the versioning tables:
php artisan seed:migrate --pretend
php artisan seed:migrate --force # run without asking
Hash Generation Only
If you want to generate hash versions for your seeders without executing them:
php artisan seed:migrate --hash-only
This is useful for:
- Initial setup in existing projects
- Verifying which seeders would be executed
- Updating the version control table without running seeders
Checking the status
To see what seed:migrate would run, without running anything:
php artisan seed:status
Last seeder run: UserSeeder (c9fd9c) at 2026-10-17 17:29:36
+---------------+----------+--------+----------------------------------+
| Seeder | Status | Hash | Risks |
+---------------+----------+--------+----------------------------------+
| ProductSeeder | new | cd5947 | [medium] insert() without upsert |
| UserSeeder | modified | dfd2ab | [medium] insert() without upsert |
+---------------+----------+--------+----------------------------------+
2 seeders pending. Run php artisan seed:migrate to execute them.
new: never run;modified: changed since its last run (the hash is the file's current one).- Risks work as in
seed:migrate(see above). - Use
--allto also list the seeders that areup to date.
How It Works
The package creates a seeder_versioning table that stores:
- Seeder class names
- Content hashes
- Execution timestamps
Every time a seeder is registered, a row is also added to the seeder_version_history table
(configurable with history_table) with the seeder name, the new hash, the previous hash
(null the first time), whether it was a --hash-only run, and when it happened. This lets
you see how many times a seeder ran and when its content changed.
If you are upgrading and already have the versions table, the history table is created
automatically on the next seed:migrate. If you manage the package migrations yourself,
publish them again with php artisan vendor:publish --tag=seeder-versioning.
When you run seed:migrate, the package:
- Scans your seeder directory
- Generates a hash for each seeder's content
- Compares against stored hashes
- Executes only modified or new seeders
Excluding seeders
DatabaseSeeder is skipped by default: it calls the other seeders, so running it would run them twice. To skip other seeders, add them to except in config/seeder-versioning.php, with or without namespace:
'except' => [ 'DatabaseSeeder', \Database\Seeders\LocalDevSeeder::class, ],
Excluded seeders are never run or registered in the versioning table.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This package is open-sourced software licensed under the MIT license.