在 composer.json 的 "scripts" 中写 migration 脚本需用 "@php artisan migrate --force" 等无交互命令,确保跨平台兼容、环境变量就绪、autoload 已刷新,并通过独立 shell 脚本或多 script 分离控制执行时机与目标库。

composer.json 里怎么写 migration 脚本?
直接在 "scripts" 段落里加一条命令,调用你项目实际使用的迁移工具(比如 Laravel 的 php artisan migrate 或 Doctrine 的 php vendor/bin/doctrine orm:schema-tool:update --force)。关键不是“能不能写”,而是**命令必须能在当前环境无交互执行**——不能卡在确认提示上。
常见写法示例(Laravel 场景):
"scripts": {
"migrate": [
"@php artisan migrate --force",
"@php artisan db:seed --force"
]
}
-
--force是必须的,否则生产环境会因未确认而中止 - 避免写成
"migrate": "php artisan migrate"—— 这样不兼容 Windows(PHP 可执行文件路径问题),@php由 Composer 自动解析为当前 PHP 二进制路径 - 如果迁移命令依赖环境变量(如
DB_HOST),确保运行composer run migrate时这些变量已加载(例如通过.env或系统级 export)
为什么 run migrate 有时不生效或报错?
最常见原因是 **当前工作目录不对** 或 **autoload 未刷新**。Composer 脚本默认在 composer.json 所在目录执行,但某些框架(如 Symfony)要求在项目根下才能正确加载配置和 autoloader。
- 运行前先执行
composer dump-autoload --optimize(尤其上线前),否则新写的迁移类可能不被识别 - 检查
artisan或bin/console是否有可执行权限(Linux/macOS);Windows 下注意换行符是否为 CRLF 导致解析失败 - 错误信息如
Class 'Database\Seeders\DatabaseSeeder' not found,大概率是 autoload 问题,不是脚本本身写错了 - 不要在脚本里写
cd ./app && php artisan migrate—— Composer 不保证 shell 命令的跨平台行为,应靠项目结构约定而非硬编码路径
如何让 migrate 只在部署时触发,且跳过本地开发?
靠环境变量控制比改脚本更安全。Composer Scripts 本身不内置环境判断,但你可以用 shell 条件或封装一层小脚本。
推荐做法:在 "scripts" 中调用一个独立的 shell 脚本(如 bin/deploy-migrate),内容如下:
#!/bin/sh
if [ "$APP_ENV" = "production" ]; then
php artisan migrate --force
else
echo "Skipping migrations: APP_ENV is not production"
fi
- 赋予执行权限:
chmod +x bin/deploy-migrate - 在
composer.json中引用:"migrate": "bin/deploy-migrate" - 这样既清晰隔离逻辑,又避免把条件判断塞进 JSON 字符串里导致转义混乱
- 注意:Docker 部署时,
APP_ENV必须在容器启动时注入,不能只靠 .env 文件(.env 在构建阶段可能未生效)
多数据库或分片场景下 scripts 怎么处理?
别试图用一个 composer run migrate 搞定所有库。Composer Scripts 不适合做流程编排,它只是快捷入口。
- 为不同库定义不同 script,比如
"migrate:tenant"、"migrate:system",各自指向明确的命令和连接配置 - 迁移顺序很重要:先主库再从库,先 schema 再 seed。用
"scripts"数组顺序控制(数组内按序执行),但跨 script 无法保证依赖关系 - 真正复杂的编排(比如某迁移失败则回滚其他库),应该交给 CI/CD 流水线或专用部署工具(Ansible、Deployer),而不是塞进 Composer
- Doctrine 用户注意:
orm:schema-tool:update是危险操作,生产环境严禁使用;必须用migrations:migrate配合显式版本管理
实际部署时,最易被忽略的是迁移命令的超时设置和锁机制。比如 Laravel 默认 migration 进程没有超时保护,长迁移可能被 systemd 或 Docker kill 掉;某些数据库(如 MySQL)在执行 DDL 时会锁表,若同时跑多个 deploy 实例,可能互相阻塞甚至死锁。这些得在运维层解决,不是改几行 composer.json 就能绕过的。










