laravel没有笼统的“回滚数据库”操作,唯一原生支持的部分回滚是php artisan migrate:rollback --step=n,它按migrations表中batch字段倒序撤销n个批次(每批对应一次migrate执行的所有迁移),不支持按文件、时间或指定名称回滚。

直接说结论: Laravel 没有“回滚数据库”这个笼统操作,只有针对迁移(migration)的精确控制;php artisan migrate:rollback 是唯一原生支持的部分回滚方式,但它按 batch 回滚,不是按文件、不是按时间、也不能跳着撤。
为什么 migrate:rollback --step=N 不是你想的“撤 N 个文件”
它实际是倒序撤销 migrations 表里 batch 字段最大的 N 批次。每批对应一次 php artisan migrate 执行时写入的所有迁移记录。
- 比如你连续跑三次
migrate,生成了batch = 1、2、3,那--step=1只撤batch=3这整批(哪怕含 7 个迁移文件) -
--step=2撤batch=3和batch=2,batch=1完全不动 - 如果某次
down()报错(例如删表前没检查外键),整个回滚中断,后续批次不会执行 - 执行前建议先查当前最大 batch:
SELECT MAX(batch) FROM migrations;
想只撤 CreateOrdersTable 和 AddTaxToInvoices 怎么办
Laravel 官方不支持按文件名或内容回滚。硬要这么做,得手动改 migrations 表,风险高、易出错。
- 先确认这两个迁移的
down()方法逻辑安全(比如删表前加Schema::hasTable()判断) - 从
migrations表查出它们的id和当前batch值 - 把这两行的
batch临时改成一个极大值(如999) - 再执行
php artisan migrate:rollback --step=1,就能只撤这俩 - 回滚完立刻把
batch改回原值,否则下次migrate会混乱
migrate:reset 和 migrate:fresh 不是“精准回滚”工具
这两个命令面向的是全量清理,和“部分回滚”目标完全冲突。
-
migrate:reset会按batch从大到小,逐个执行所有已注册迁移的down()—— 它清空全部历史,不是选几个 -
migrate:fresh更激进:直接删库重建(靠DB::getDoctrineSchemaManager()->listTables()获取表名),完全不走down()流程 - 两者都会触发所有
down(),只要其中任意一个失败(比如删被外键引用的表),整个流程就卡住 - 生产环境禁用
--force:它跳过确认提示,但不校验down()是否真能安全执行
事务级回滚和迁移回滚完全是两回事
别混淆 DB::transaction() 和 migrate:rollback。前者管运行时 SQL 操作原子性,后者管数据库结构版本。
-
DB::transaction()只在闭包抛未捕获异常时自动回滚,依赖PDO::ERRMODE_EXCEPTION配置 - 如果在事务里调用了 HTTP 请求、文件写入等不可逆操作,DB 回滚了,那些外部动作还在,业务就错位了
- 嵌套
DB::transaction()不是真正嵌套事务,MySQL 层面仍是单 BEGIN;内层异常被外层catch住,变更就真的提交了 - 需要局部回滚?得显式用
DB::beginTransaction()+DB::rollBackTo('sp_name'),但 Laravel 不自动建 savepoint
最常被忽略的一点:迁移回滚成败,90% 取决于 down() 方法是否真正可逆。比如 up() 加了唯一索引,down() 就得先删数据再删索引;up() 改了字段类型,down() 得考虑旧数据能否无损转回。写迁移时没想清楚这点,回滚时基本必挂。











