In Laravel, how do migrate:rollback, migrate:reset, migrate:refresh and migrate:fresh differ, and which of them never calls down()?
answer
- rollback: latest batch only
- reset: every down(), newest first
- refresh: reset then migrate
- fresh: drop all tables via db:wipe
- prohibitDestructiveCommands in production
basics
~10 srollback runs down() for the latest batch; reset runs down() for every migration; refresh does reset (or a --step rollback) and migrates again; fresh drops every table with db:wipe and migrates, never calling down().
solid answer
~40 s`migrate:rollback` reverses the **latest batch** by calling each migration's `down()`; `--step=3` reverses the last three migrations and `--batch=2` a specific batch. `migrate:reset` calls `down()` on **every** recorded migration, newest first. `migrate:refresh` runs a reset (or a rollback with `--step`) and then `migrate`, so it depends on every `down()` being correct. `migrate:fresh` skips `down()` entirely: it runs `db:wipe` to drop all tables, then `migrate`, which makes it faster and immune to broken `down()` methods but destroys every table on the connection, including ones no migration created. `migrate --step` records each migration in its own batch so later rollbacks can go one at a time. In production these commands ask for confirmation unless `--force` is passed, and `DB::prohibitDestructiveCommands()` blocks fresh, refresh, reset, rollback and `db:wipe` outright.
code
bash · 5 linesphp artisan migrate --step # each migration gets its own batch
php artisan migrate:rollback # undo the latest batch
php artisan migrate:rollback --step=2
php artisan migrate:refresh # every down(), then migrate
php artisan migrate:fresh # db:wipe, then migrate; no down()go deeper
Recall that rollback undoes the last batch, reset undoes all, refresh resets and re-runs, and fresh drops every table.
Explain batches, --step and --batch, and why fresh never calls down() while refresh depends on it.
Protect production with prohibitDestructiveCommands, use refresh to test down() methods, and deploy with --step when rollbacks must be surgical.
Decide whether production rollbacks are ever allowed or every fix ships as a new forward migration.
## The shared vocabulary: batches Every time `php artisan migrate` runs, the migrations it applies are recorded in the `migrations` table under one new **batch** number. The rollback family of commands works in terms of those batches and of each migration's `down()` method. ## The four commands | Command | What it does | Calls `down()`? | |---|---|---| | `migrate:rollback` | reverses the latest batch | yes, for that batch | | `migrate:reset` | reverses every recorded migration | yes, for all | | `migrate:refresh` | reset (or rollback with `--step`), then `migrate` | yes, for all or the stepped ones | | `migrate:fresh` | `db:wipe` drops every table, then `migrate` | **no** | ### `migrate:rollback` - With no options, reverses the migrations of the **latest batch**, newest first. - `--step=3` reverses the last three migrations regardless of batch. - `--batch=2` reverses the migrations recorded in batch 2. - `--pretend` prints the SQL the `down()` methods would run without running it. ### `migrate:reset` Reverses **everything**, calling `down()` on every recorded migration. If one `down()` is wrong, for example it forgets a table, the reset leaves that table behind and the next `migrate` fails when `up()` tries to create it again. ### `migrate:refresh` A convenience wrapper: reset (or `--step=N` rollback) followed by `migrate`. It is a good test of your `down()` methods, precisely because a broken one makes it fail. ### `migrate:fresh` Does not use `down()` at all. It calls `db:wipe`, which drops **all tables** on the connection (with `--drop-views` and, on PostgreSQL, `--drop-types` for those too), then runs `migrate` from scratch. Because it ignores `down()`, it works even when some migrations are not reversible, which makes it the usual command in local development. It also drops tables that no migration created, so it is never something to point at a shared database. ## Controlling batches when you migrate `php artisan migrate --step` records **each** migration as its own batch. Afterwards, `migrate:rollback` without options undoes only the last migration rather than everything that was deployed together. ## Guard rails for production 1. **Confirmation.** When the environment is production, these commands and `migrate` itself ask for confirmation; `--force` skips the prompt, which is what automated deploys pass to `migrate`. 2. **Prohibition.** `DB::prohibitDestructiveCommands()` in `AppServiceProvider::boot()`, typically called with `$this->app->isProduction()`, makes `migrate:fresh`, `migrate:refresh`, `migrate:reset`, `migrate:rollback` and `db:wipe` refuse to run. 3. **Seeding flags** such as `--seed` exist on `migrate`, `migrate:fresh` and `migrate:refresh`, but seeding is a separate concern with its own commands. ```php use Illuminate\Support\Facades\DB; public function boot(): void { DB::prohibitDestructiveCommands($this->app->isProduction()); } ``` ## Reading the batch history `php artisan migrate:status` prints every migration file with `Ran` or `Pending` and the batch number it ran in. Before any rollback in a shared environment, reading that list is the quickest way to see exactly which files `migrate:rollback` would reverse, and whether a `--step` value reaches further back than intended. Remember that the batch number is assigned per `migrate` run, so two deploys that each ran one migration produce two batches, while one deploy that ran five migrations produces a single batch of five unless `--step` was used. ## Choosing between them - **Local development, schema in flux:** `migrate:fresh`. - **Checking that `down()` methods really reverse `up()`:** `migrate:refresh` in CI or locally. - **Undoing a bad deploy's migration:** `migrate:rollback`, ideally after deploying with `--step` so the rollback is surgical. - **Production data:** none of the destructive ones; write a new forward migration instead.
- migrate:refresh fails with 'table listings already exists', but migrate:fresh works. What does that tell you?Some migration's `down()` does not undo its `up()`, for example it never drops `listings`. Refresh relies on every `down()`, so the stray table survives the reset and the create fails. Fresh ignores `down()` and wipes all tables, hiding the bug. Fix the `down()` method rather than switching commands.
- After deploying three migrations together, only the last one is faulty. How do you roll back just that one?Run `php artisan migrate:rollback --step=1`, which reverses the most recent migration regardless of batch. Deploying with `migrate --step` in the first place makes each migration its own batch, so a plain `migrate:rollback` does the same.
saying these in an interview costs you the question
- Believes migrate:fresh runs every down() method first
- Thinks migrate:rollback reverses all migrations by default
- Runs migrate:fresh against a shared or production database
- Assumes migrate:fresh only drops tables its migrations created
- Thinks --force disables DB::prohibitDestructiveCommands()