不该自己封装 migrate:rollback,因Laravel迁移依赖时间戳顺序和migrations表原子记录,手动回滚易致状态不一致;原生命令已支持--step精准控制,且含锁机制、错误中断等保障,自定义命令反而增加并发冲突与维护风险。

直接写一个“回滚指定迁移”的自定义 Artisan 命令不可靠,Laravel 的 migrate:rollback 本身已支持精准控制,硬造轮子反而容易破坏迁移链完整性。
为什么不该自己封装 migrate:rollback
Laravel 的迁移系统依赖时间戳顺序和 migrations 表的原子记录,任何绕过原生逻辑的手动回滚(比如在自定义命令里调用 Schema::drop() 或删表)都会导致状态不一致。
-
migrate:rollback --step=1已能精确回退最后一次执行的迁移组(可能含多个文件),无需额外封装 - 手动实现
down()逻辑时,若某迁移没写健壮的判断(如Schema::hasTable()),命令一跑就崩,而原生命令至少会报错并停住 - 自定义命令无法自动感知 Laravel 的迁移锁机制(
php artisan migrate --force被禁用时的保护),容易在部署中引发并发冲突
真正需要自定义命令的场景:带业务逻辑的回滚前检查
比如你有个迁移加了唯一索引,但回滚前要确认线上数据是否允许删该字段——这种校验不能塞进 down(),得在 migrate 执行前做。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 用
php artisan make:command CheckBeforeRollback创建命令 - 在
handle()中查DB::table('users')->whereNotNull('phone')->count(),非零就$this->error('存在 phone 数据,禁止回滚')并返回1 - 把它加到部署脚本里,放在
php artisan migrate:rollback前执行,而不是试图替代它
如果坚持要“按文件名回滚”,必须处理这些坑
Laravel 不提供按迁移文件名回滚的能力,因为时间戳才是权威顺序。强行实现需解析文件名、匹配 migrations 表、手动删记录——风险极高。
- 先查
DB::table('migrations')->where('migration', 'like', '%add_phone_to_users_table%')->get(),注意通配符要转义 - 拿到
batch值后,再查同 batch 的所有记录,确保整组一起删,否则留下脏数据 - 删
migrations表记录后,必须手动执行对应down()方法(用Artisan::call('migrate:rollback', ['--step' => 1])不行,它只认当前最高 batch) - MySQL 8.0+ 下,
Schema::dropIfExists('users')在down()里安全,但Schema::dropColumn('users', 'phone')必须包if (Schema::hasColumn('users', 'phone')) { ... }
最常被忽略的是:迁移文件一旦执行过,修改其中的 up() 或 down() 再跑 migrate 完全无效——Laravel 只看文件名是否已在 migrations 表里。所谓“回滚再重跑”不是改旧文件,而是新建迁移补救。

















