Composer 的 scripts 可在 post-install-cmd 和 post-update-cmd 中执行迁移命令,支持 Laravel、Symfony、Phinx 等框架,通过环境变量控制行为,失败时中断流程保障 CI 安全。

composer.json 里怎么写 post-install-cmd 和 post-update-cmd 脚本
Composer 的 scripts 是最轻量的自动化入口,适合在依赖安装/更新后自动跑迁移。它不依赖 Laravel 或 Symfony 的命令系统,纯靠 shell 命令或 PHP 脚本触发。
关键点:这些钩子默认在项目根目录执行,所以 php artisan migrate 能直接运行的前提是项目已装好 Laravel 并且 artisan 在当前路径下;否则会报 Command "migrate" is not defined。
- 只对 Laravel 项目有效?不是。只要脚本能调通,你可以写
php bin/console doctrine:migrations:migrate(Symfony)或php vendor/bin/phinx migrate(Phinx) - 想区分开发/生产环境?用
APP_ENV环境变量控制,例如:"php artisan migrate --force --env=production",但注意--force在生产必须显式加,否则交互式确认会卡住 - 迁移失败会导致
composer install整体退出(exit code ≠ 0),CI 流程会中断——这反而是你想要的行为,避免部署半截数据库
{
"scripts": {
"post-install-cmd": [
"@php artisan migrate --no-interaction"
],
"post-update-cmd": [
"@php artisan migrate --no-interaction"
]
}
}
自定义 Composer 命令:用 ScriptHandler 实现跨框架兼容
硬写 shell 命令容易耦合框架,换种方式:写一个 PHP 类,让 Composer 直接调它的静态方法。这样能复用应用的容器、配置、环境判断逻辑,也方便加日志或 try/catch。
比如新建 scripts/DatabaseMigrator.php:
&1');
echo $result;
if (strpos($result, 'Nothing to migrate') === false && strpos($result, 'Migrated:') === false) {
throw new RuntimeException("Migration failed:\n{$result}");
}
}
}
然后在 composer.json 中注册:
{
"autoload": {
"psr-4": {
"App\\Scripts\\": "scripts/"
}
},
"scripts": {
"migrate-db": [
"App\\Scripts\\DatabaseMigrator::run"
]
}
}
之后就能手动执行:composer migrate-db,也可被其他脚本调用(如 "post-update-cmd": ["@migrate-db"])。
为什么 migrate 经常卡在 “Waiting for lock” 或提示 “Table doesn’t exist”
这不是 Composer 问题,而是迁移命令本身在并发或环境不一致时暴露的底层矛盾。常见于 CI/CD 或多实例部署场景。
-
Waiting for lock:Laravel 默认用文件锁(storage/framework/cache/data/...),CI 容器无共享存储 → 改用 Redis 或 database 驱动锁,或在config/cache.php中设'default' => 'array'(仅限单次执行) -
Base table or view not found:说明migrations表本身不存在,而 Laravel 迁移命令默认假设它已存在。首次部署需先运行php artisan migrate:install,再跑migrate - 本地开发没问题,CI 报错?检查
.env是否被 git 忽略,CI 环境是否漏配DB_DATABASE或权限不足(如 MySQL 用户没 CREATE 权限)
真正要小心的是“自动迁移”的边界
自动执行 migrate 很方便,但线上环境永远不该依赖它完成关键变更。比如加非空字段、删列、改索引类型——这些操作在大数据表上会锁表数分钟,而 Composer 钩子没有超时控制,会拖垮整个部署流程。
更稳妥的做法是:
- 把高危迁移拆成独立命令,如
composer migrate-safe(只跑 DDL 安全操作)和composer migrate-risky(需人工确认) - 在 CI 中禁用所有自动迁移,改由部署脚本分步执行:先
php artisan migrate:status检查差异,再人工审核 SQL - 始终保留
php artisan migrate:rollback --step=1能回退的能力,别依赖 “下次 deploy 覆盖”
自动化的价值在于减少重复劳动,不是替代人的判断。数据库结构变更永远是最难 rollback 的环节,脚本越顺滑,越要盯紧它的输出和副作用。










