如何通过Laravel生成并执行数据库迁移文件?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1268个文字,预计阅读时间需要6分钟。
文件名修改不会影响执行逻辑,但Laravel会检查文件名是否包含特定前缀,如create_或add_,来自动填充模板内容。例如,php artisan make:migration create_users_table会生成带有Schema::create(')的空表结构;而php artisan make:migration add_phone_to_users_table则默认使用Schema::table('),适用于添加字段。
常见错误是手误写成 create_user_table(少个 s),结果生成的迁移里表名是 user,上线后报 Table 'user' doesn't exist —— 因为 Laravel 默认复数表名,且多数约定是 users。
- 命名建议统一用复数 +
_table后缀,如create_posts_table - 如果想跳过自动模板、完全自定义,加
--plain参数:php artisan make:migration rename_user_email --plain - 别依赖文件名推断逻辑,打开生成的文件第一眼确认
up()里调用的是create()还是table()
执行迁移前,先看 php artisan migrate:status 和 php artisan migrate --pretend
直接 php artisan migrate 很快,但出错就难回退。尤其在生产环境或多人协作时,你不知道别人有没有提交新迁移却没跑,也不知道当前本地数据库状态是否和代码一致。
php artisan migrate:status 会列出所有迁移文件及其 Ran? 状态,一眼看出哪些漏了、哪些失败了;php artisan migrate --pretend 则把实际要执行的 SQL 打印出来,不真跑 —— 这能帮你提前发现字段类型冲突、索引重名、外键约束缺失等硬伤。
-
--pretend输出的 SQL 是基于当前数据库连接配置的,所以务必先确认.env里DB_DATABASE指向的是你打算操作的库 - 如果看到
ALTER TABLE users ADD COLUMN phone VARCHAR(20),但users表已经有百万行,就得警惕:这个操作在 MySQL 5.6+ 虽支持 online DDL,但依然可能锁表几秒,得避开高峰 - 状态命令不显示失败详情,若某次迁移卡住,先查
failed_jobs表或日志里的具体异常,再决定是rollback还是手动修复
down() 方法不是必须写全,但删字段/删表必须显式处理
Laravel 不强制要求每个迁移都实现可逆逻辑,down() 留空也能跑 migrate。但如果你写了 down() 却没覆盖所有 up() 的副作用,比如只删了字段却忘了删对应索引,下次回滚再迁移就会报 Index name already exists。
更麻烦的是,Laravel 8+ 默认禁用 dropColumn() 在 SQLite 和某些旧版 MySQL 上的使用 —— 它底层靠重建表实现,容易丢数据或触发严格模式报错。
- 新增字段、索引、外键,
down()一般只需反向操作:用dropColumn()、dropIndex()、dropForeign() - 删除字段?优先考虑保留字段、加注释标记废弃,而不是真删 —— 回滚风险高,ORM 层也可能残留引用
- 真要删,MySQL 8.0+ 可用
dropColumn(),但得确保DB_STRICT_MODE=true关闭,否则可能因默认值校验失败
多环境部署时,migrate 命令必须加 --force 才能在生产环境运行
Laravel 默认在非本地环境禁止执行迁移,防止误操作炸掉线上库。所以你在服务器上跑 php artisan migrate,大概率看到 Nothing to migrate. 或直接报错,不是没新迁移,而是被安全机制拦住了。
这个开关藏在 APP_ENV 和 APP_DEBUG 之后,核心逻辑是:只要 APP_ENV 不是 local,就必须显式加 --force。而且它不接受 --force=true 这种写法,只能是 --force。
- CI/CD 流水线里记得加参数:
php artisan migrate --force --no-interaction,后者避免交互式确认卡住 - 别在
.env里临时改APP_ENV=local来绕过,这会让日志、缓存、异常页面全变成开发态,暴露敏感信息 - 如果用了多数据库连接(比如 tenants),还得指定
--database=tenant1,否则默认只跑主库
迁移不是“写完就能跑”,它是数据库 schema 的版本控制动作,每一步背后都有隐式依赖和环境假设。最常出问题的,往往不是语法错误,而是没看清当前连接的是哪个库、没确认 up() 和 down() 是否真正对称、或者忘了生产环境那道 --force 的门禁。
本文共计1268个文字,预计阅读时间需要6分钟。
文件名修改不会影响执行逻辑,但Laravel会检查文件名是否包含特定前缀,如create_或add_,来自动填充模板内容。例如,php artisan make:migration create_users_table会生成带有Schema::create(')的空表结构;而php artisan make:migration add_phone_to_users_table则默认使用Schema::table('),适用于添加字段。
常见错误是手误写成 create_user_table(少个 s),结果生成的迁移里表名是 user,上线后报 Table 'user' doesn't exist —— 因为 Laravel 默认复数表名,且多数约定是 users。
- 命名建议统一用复数 +
_table后缀,如create_posts_table - 如果想跳过自动模板、完全自定义,加
--plain参数:php artisan make:migration rename_user_email --plain - 别依赖文件名推断逻辑,打开生成的文件第一眼确认
up()里调用的是create()还是table()
执行迁移前,先看 php artisan migrate:status 和 php artisan migrate --pretend
直接 php artisan migrate 很快,但出错就难回退。尤其在生产环境或多人协作时,你不知道别人有没有提交新迁移却没跑,也不知道当前本地数据库状态是否和代码一致。
php artisan migrate:status 会列出所有迁移文件及其 Ran? 状态,一眼看出哪些漏了、哪些失败了;php artisan migrate --pretend 则把实际要执行的 SQL 打印出来,不真跑 —— 这能帮你提前发现字段类型冲突、索引重名、外键约束缺失等硬伤。
-
--pretend输出的 SQL 是基于当前数据库连接配置的,所以务必先确认.env里DB_DATABASE指向的是你打算操作的库 - 如果看到
ALTER TABLE users ADD COLUMN phone VARCHAR(20),但users表已经有百万行,就得警惕:这个操作在 MySQL 5.6+ 虽支持 online DDL,但依然可能锁表几秒,得避开高峰 - 状态命令不显示失败详情,若某次迁移卡住,先查
failed_jobs表或日志里的具体异常,再决定是rollback还是手动修复
down() 方法不是必须写全,但删字段/删表必须显式处理
Laravel 不强制要求每个迁移都实现可逆逻辑,down() 留空也能跑 migrate。但如果你写了 down() 却没覆盖所有 up() 的副作用,比如只删了字段却忘了删对应索引,下次回滚再迁移就会报 Index name already exists。
更麻烦的是,Laravel 8+ 默认禁用 dropColumn() 在 SQLite 和某些旧版 MySQL 上的使用 —— 它底层靠重建表实现,容易丢数据或触发严格模式报错。
- 新增字段、索引、外键,
down()一般只需反向操作:用dropColumn()、dropIndex()、dropForeign() - 删除字段?优先考虑保留字段、加注释标记废弃,而不是真删 —— 回滚风险高,ORM 层也可能残留引用
- 真要删,MySQL 8.0+ 可用
dropColumn(),但得确保DB_STRICT_MODE=true关闭,否则可能因默认值校验失败
多环境部署时,migrate 命令必须加 --force 才能在生产环境运行
Laravel 默认在非本地环境禁止执行迁移,防止误操作炸掉线上库。所以你在服务器上跑 php artisan migrate,大概率看到 Nothing to migrate. 或直接报错,不是没新迁移,而是被安全机制拦住了。
这个开关藏在 APP_ENV 和 APP_DEBUG 之后,核心逻辑是:只要 APP_ENV 不是 local,就必须显式加 --force。而且它不接受 --force=true 这种写法,只能是 --force。
- CI/CD 流水线里记得加参数:
php artisan migrate --force --no-interaction,后者避免交互式确认卡住 - 别在
.env里临时改APP_ENV=local来绕过,这会让日志、缓存、异常页面全变成开发态,暴露敏感信息 - 如果用了多数据库连接(比如 tenants),还得指定
--database=tenant1,否则默认只跑主库
迁移不是“写完就能跑”,它是数据库 schema 的版本控制动作,每一步背后都有隐式依赖和环境假设。最常出问题的,往往不是语法错误,而是没看清当前连接的是哪个库、没确认 up() 和 down() 是否真正对称、或者忘了生产环境那道 --force 的门禁。

