最常导致数据库状态和迁移历史不一致的源头是迁移文件生成后未执行 php artisan migrate 就修改代码;laravel 依赖 migrations 表记录已执行迁移,而非文件内容比对。

迁移文件生成后没执行 php artisan migrate 就改了代码
这是最常导致数据库状态和迁移历史不一致的源头。Laravel 不靠文件内容比对,而是靠 migrations 表里的 batch 和 migration 字段记录「哪些文件已跑过」。改了已执行过的迁移文件(比如加了个字段),再跑 php artisan migrate 不会生效——它只找没记录在表里的新文件。
- 新增字段/索引,必须新建迁移:用
php artisan make:migration add_status_to_users_table - 已执行的迁移文件绝对不要手动修改;哪怕只是拼写错误,也应新建一个修复迁移(如
fix_typo_in_users_email_column) - 本地调试时误改了已执行迁移?可临时用
php artisan migrate:rollback --step=1回退,再重新生成正确迁移
php artisan migrate:fresh 在生产环境直接运行
这个命令会删掉所有表再重跑全部迁移,等价于 DROP DATABASE + CREATE DATABASE + migrate。它没有确认提示,也不检查是否在生产环境——只要 APP_ENV=production 且配置了生产数据库,它就真删。
- 本地开发可放心用,但 CI/CD 或部署脚本里必须禁止该命令出现在生产流程中
- 线上变更只能走增量迁移:每改一个字段、加一张表,都对应一个独立的
up()/down() - 想清空测试数据?用
php artisan db:seed配合--force,而不是刷库
在 up() 里写业务逻辑,比如调用模型或发通知
迁移不是启动 Laravel 应用上下文的时机。up() 和 down() 运行在 Artisan 命令行环境中,模型、队列、事件监听器可能未加载或行为异常——尤其当迁移涉及刚新增的字段,而模型还没加 $fillable 或 $casts 时,直接 User::all()->each(...) 很容易报错或静默失败。
- 数据转换类操作(如给旧用户补默认头像)应写在单独的 Artisan 命令里,迁移只负责结构变更
- 如果真需要在迁移中操作数据,确保用原生查询:
DB::table('users')->whereNull('avatar')->update(['avatar' => 'default.png']) - 避免在迁移里调用
event()、dispatch()、app()等依赖完整容器的函数
不同团队成员的迁移文件时间戳冲突
Laravel 迁移文件名形如 2023_05_12_102832_create_posts_table.php,前缀是时间戳。多人并行开发时,若两人在同一秒生成迁移,文件名重复,php artisan migrate 会跳过其中一个(报 Migration not found),但不会报错提示。
- 生成迁移前先
php artisan migrate:status看最新时间戳,手动生成时把秒数+1(如从102832改成102833) - 更稳妥的做法:用 Git 分支隔离迁移开发,合并前人工检查迁移文件名是否连续、无重复
- CI 流程里可加校验脚本,grep 所有迁移文件名,用
sort -u | wc -l对比总数,不等就失败
迁移不是数据库版本管理的银弹。真正难的不是写 Schema::create(),而是让所有人对「哪次变更对应哪张表、哪个字段、哪条记录」有确定共识——这取决于命名是否清晰、提交信息是否具体、回滚路径是否验证过。别省那几秒钟写个准确的迁移名。








