webparking/laravel-db-rebuild
A Laravel package that adds a db:rebuild Artisan command to quickly reset your database by dropping all tables, rerunning migrations, and optionally seeding. Ideal for local development and testing, it makes repeatable database refreshes fast, consistent, and simple without manual SQL.
Install with Composer and let Laravel's package auto-discovery wire it up:
composer require webparking/laravel-db-rebuild --dev
On Laravel 5.5+ the service provider is discovered automatically via extra.laravel.providers in the package's composer.json. If you are on an older framework version or have discovery disabled, register Webparking\DbRebuild\DbRebuildServiceProvider manually in config/app.php.
Where to look first. After install, run:
php artisan list | grep -i rebuild
That prints the exact command signature this version exposes (typically db:rebuild), its options, and its description. Read the command class itself under vendor/webparking/laravel-db-rebuild/src/Commands/ — the implementation is small, and confirming exactly which options exist (--seed, --class, --connection, --force) is faster than guessing from the README.
First use case. Point it at your local development database and verify the destructive behaviour before you trust it:
# Confirm which connection and database you are pointed at
php artisan db:show # or your tinker / .env check
# First run: no seeding, just drop + migrate
php artisan db:rebuild
Success looks like: all tables dropped, every migration in database/migrations/ replayed from scratch, and an empty schema matching php artisan migrate:fresh output. Once that is green, add seeding:
php artisan db:rebuild --seed
If either run surprises you — wrong connection hit, tables you did not expect to be dropped — stop and read the Gotchas section before using it in CI.
Daily local reset loop. The canonical workflow is "pull main, reset to a known schema, seed, start working":
git pull && composer install && php artisan db:rebuild --seed
Wire it into a Composer script so every developer runs the same thing:
"scripts": {
"db:reset": [
"@php artisan db:rebuild --seed"
]
}
Then composer db:reset. This is the single highest-value integration — it collapses a multi-step migrate:rollback/migrate:fresh/db:seed dance into one memorable command and removes the "it works on my machine, my migrations are out of order" class of bug.
CI pipeline step. In CI you want a deterministic database before the test suite runs. Two common shapes:
# Option A: rebuild before the suite, no seeding
- run: php artisan db:rebuild
# Option B: rebuild with a minimal seeder for integration tests
- run: php artisan db:rebuild --seed --class=Database\\Seeders\\CiSeeder
Option A pairs well with a test trait (RefreshDatabase or DatabaseTransactions) that handles per-test isolation; the rebuild is only there to guarantee the schema exists and migrations apply cleanly on a fresh database — which catches migration ordering bugs that a developer's long-lived local DB silently hides. Option B is useful for smoke/e2e tests that assert against seeded reference data. Keep the CI seeder a dedicated class: never reuse DatabaseSeeder if it contains demo data or slow fixtures.
Seed strategy per environment. Define seeders that match intent, not environment:
| Context | Seeder | Why |
|---|---|---|
| Local dev | DatabaseSeeder |
Full demo dataset: users, sample orders, images |
| CI / test | Minimal seeder (e.g. CiSeeder) |
Only reference/lookup rows required for assertions |
| Demo / staging | DemoSeeder |
Realistic-but-fake volume for click-through |
Pass the class explicitly: --class=Database\\Seeders\\CiSeeder. This keeps DatabaseSeeder free to call slower or faker-heavy seeders without CI paying the cost.
Targeting a non-default connection. If your app has multiple connections (mysql, pgsql, testing), pass --connection=... rather than mutating DB_CONNECTION in the shell. Explicit is safer, especially when the command drops tables — you want that target visible in the invocation line, not buried in an env override:
php artisan db:rebuild --connection=pgsql --seed
Paired with migrate:fresh for comparison. migrate:fresh drops all tables and re-runs migrations; db:rebuild provides the same outcome plus an optional seeder in one command. Adopt db:rebuild as the canonical reset and use migrate:fresh only when you specifically need --step/--path behaviours that the rebuild command does not expose.
Guardrails in application code. Because this command is irreversibly destructive, add belt-and-braces at the application layer:
--dev so it is absent from production images.AppServiceProvider::boot(), abort if app()->environment('production') and the rebuild command is somehow registered — a cheap, defensive check.php artisan db:show (or check .env) before invoking the command.This drops tables — there is no undo. Unlike migrate:refresh, which rolls back in reverse order, the rebuild drops the schema outright. That means no down() methods run, so any seeder or fixture that relies on migration down() side effects is silently bypassed. Treat the command as a one-way door and always confirm DB_DATABASE before running it locally.
2020-era package vs. Laravel 11/12. The last release was September 2020, which predates Laravel 9, illuminate/* split constraints, and PHP 8.x attribute registration. Expect the package to still function (it is a thin wrapper over Artisan + DB + migration commands) but verify on a spike branch:
composer require webparking/laravel-db-rebuild:^1.2 --dev -W
If Composer forces an unrelated upgrade, stop — the maintenance tax is not worth the convenience over a two-line migrate:fresh && db:seed alias. If it resolves cleanly, pin the exact version in composer.json and commit the lockfile; do not let ^1.2 float in CI.
Foreign key constraint failures during drop. Some drivers (notably older MySQL and SQLite with foreign_key_constraints on) will refuse to DROP TABLE when FK dependencies exist, or will refuse to re-create them without explicit ordering. If the command fails mid-drop with a constraint error, check:
config/database.php → connections.mysql.foreign_key_constraints and driver version.SET FOREIGN_KEY_CHECKS=0 is being issued by the command — if the implementation doesn't, you may need to run it yourself before invoking the command:mysql -e 'SET FOREIGN_KEY_CHECKS=0' "$DB_DATABASE"
php artisan db:rebuild
This is the most common failure mode in practice and is worth documenting in your team's runbook.
Seeder class resolution is fiddly. If --class errors with "Target class [...] does not exist", it is almost always a namespace-escaping problem on the CLI. On bash: --class=Database\\Seeders\\CiSeeder. On PowerShell (Windows developers): --class="Database\Seeders\CiSeeder". Prefer a short class name if the package's resolver supports it, or wrap the invocation in a Composer script where the shell is not involved.
DDL inside transactions varies by driver. On MySQL, DROP TABLE and CREATE TABLE implicitly commit, so if the command tries to wrap the whole rebuild in DB::transaction() (or you add that yourself in a wrapper), you can get a half-migrated state on failure. On Postgres and SQLite, DDL is transactional — a failed rebuild leaves you with the pre-rebuild schema. Test the failure mode you actually get in your environment before relying on atomicity.
CI connection hygiene. In CI, DB_CONNECTION frequently defaults to sqlite (:memory:) when no DB_* env vars are set. That means db:rebuild may succeed against a throwaway in-memory database while your integration suite runs against MySQL, giving you a false green. Always export explicit DB_CONNECTION/DB_HOST/DB_DATABASE before the rebuild step, and log the connection name (php artisan db:show output) in the CI log so it is auditable.
Testing your own rebuild command. If you wrap db:rebuild (see extension points below), test it against a real database, not a mock. Mocking DB::statement and asserting DROP TABLE was called gives you false confidence — the failure modes are driver-level. Use a dedicated testing connection and assert schema state after the rebuild, not the queries it issued.
Extension points. Two patterns are cheap and effective when the stock command does not fit:
App\Console\Commands\RebuildDatabase extends Webparking\DbRebuild\Commands\... and override handle() to add pre-flight checks (environment guard, confirmation prompt, db:show log line) before calling parent::handle(). This is the cleanest way to add a production guard without forking.SET FOREIGN_KEY_CHECKS, running a second command), a Composer script or Makefile target is more maintainable than modifying the package. The package remains upgradeable; your orchestration stays in your repo.Debugging a partial rebuild. If the command exits mid-run, inspect the state before rerunning:
php artisan migrate:status
Tables that exist but whose migrations are not marked "Ran" indicate the drop phase succeeded and the migrate phase failed. This is usually a broken migration or a missing FK on a dropped-and-recreated table — the error text will point at the failing migration. Do not rerun blindly; fix the migration first, or the second run will fail the same way.
Abandonment risk is the real long-term gotcha. With a single release in 2020 and low star count, assume you will eventually fork or replace this. Keep your invocation isolated to one Composer script or one subclass so that a future migration to a two-line migrate:fresh && db:seed alias, or to a maintained alternative, is a one-file change rather than a sweep through every developer's shell history and every CI config.
How can I help you explore Laravel packages today?