laravel数据库迁移是用php代码描述表结构变更的版本控制机制,通过up()和down()方法实现可重复、可回退、跨环境一致执行;必须新建迁移处理变更,禁止修改已执行文件,migrate按时间戳顺序执行并记录批次,rollback按批回退,fresh清空重跑但生产禁用。

因为直接改数据库结构会破坏版本一致性,而迁移文件让结构变更可追溯、可回滚、可协作。
迁移文件本质是数据库的“Git提交”
每个 migrate 命令执行的不是 SQL 脚本,而是 PHP 代码里定义的结构操作;Laravel 用 migrations 表记录已执行的迁移文件名和时间戳,靠这个判断哪些该跑、哪些跳过。这意味着:
- 同一套迁移文件,在本地、测试、生产环境执行,只要
.env配置正确,生成的表结构完全一致 -
php artisan migrate:rollback不是猜哪条 SQL 改了什么,而是精准调用对应迁移的down()方法 - 多人协作时,A 提交了
2025_06_10_120000_add_status_to_orders.php,B 拉取后只需php artisan migrate,无需手动执行 ALTER
为什么不能直接在 MySQL 里改字段?
你改完 ALTER TABLE users ADD COLUMN phone VARCHAR(20),同事不知道——他下次 php artisan migrate 会失败,因为他的迁移文件里没这列,模型或验证规则可能直接报错。更麻烦的是:
- 生产环境回滚时,没有对应
down()逻辑,只能手动DROP COLUMN,极易遗漏或出错 - 迁移文件里用
$table->string('phone', 20)->nullable(),Laravel 自动适配 MySQL/PostgreSQL/SQLite 的语法差异;手写 SQL 就得自己处理兼容性 -
php artisan migrate:fresh --seed可一键重建整个库(含填充),这在 CI/CD 或本地重置环境时省掉大量人工
5.5 版本下特别要注意的兼容点
Laravel 5.5 的迁移构造器不支持某些新语法(比如 json 字段的完整约束、enum 类型的跨 DB 兼容写法),容易踩坑:
- 不要在
up()里写$table->enum('status', ['draft', 'published'])—— MySQL 可行,但 SQLite 会报错;改用$table->string('status')->in(['draft', 'published'])(需配合模型校验) -
$table->timestamps()默认生成created_at和updated_at,但 5.5 不自动设DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,如需此行为,得显式加$table->timestamp('created_at')->useCurrent(); - 如果用了
php artisan make:migration add_phone_to_users_table --table=users,生成的文件里是Schema::table(),别误写成Schema::create(),否则运行时报SQLSTATE[42S01]: Base table or view already exists
真正难的不是写迁移,而是让每次 up() 和 down() 都能安全往返——比如添加唯一索引后,down() 必须先删索引再删字段,顺序反了就失败。这种细节,只有在反复重跑 migrate:refresh 时才会暴露出来。











