ThinkPHP数据库迁移需三步:安装topthink/think-migration扩展、在config/console.php注册命令类、运行php think list验证;迁移文件须置于database/migrations/且命名规范;up()与down()必须成对编写;多环境部署需同步迁移文件并校验think_migration表版本。

ThinkPHP 的数据库迁移不是开箱即用的功能,必须手动安装扩展、配置命令、初始化状态表,三步缺一不可;跳过任意一步都会卡在 “Command not found” 或 “No migrations to run” 上。
php think migrate:run 报错 Command "migrate:run" is not defined
这是最常遇到的第一道坎,根本原因不是命令写错了,而是 topthink/think-migration 没装或没注册进命令系统。
- 先执行
composer require topthink/think-migration:^3.0(TP6.1+ 必须用 ^3.0,^2.0 仅兼容 TP6.0.7 及更早) - 检查
config/console.php中的'commands'数组是否包含'\think\migration\Command::class';老项目若无此配置项,需手动添加 - 运行
php think list,确认输出里有migrate:install、migrate:run等条目;没有就说明注册失败,别急着写迁移文件
php think migrate:run 提示 “No migrations to run”
这个提示极具误导性——它不表示没文件,而大概率是路径、命名或类定义没对上。
- 迁移文件必须放在
database/migrations/目录下,不是app/database/或database/migrate/ - 文件名必须严格为
YYYYMMDDHHIISS_*.php格式(如20230101000000_create_user_table.php),Windows 下尤其注意隐藏空格或 UTF-8 BOM 头 - 类名必须与文件名中
_后的部分一致(如CreateUserTable),且继承think\migration\Migrator,不能是Phinx\Migration\AbstractMigration - 改过
config/migration.php里的'path'配置?记得清空runtime/cache/,否则旧路径缓存会生效
up() 里加了字段,rollback 后字段还在
这不是 bug,是你没写对应的 down() 逻辑。ThinkPHP 迁移不会自动推断逆向操作,down() 是强制要求,不是可选项。
立即学习“PHP免费学习笔记(深入)”;
- 如果
up()里调用了$this->table('user')->addColumn('phone'),down()就必须显式写$this->table('user')->dropColumn('phone') - MySQL 5.7+ 的某些存储引擎(如 MyISAM)不支持
DROP COLUMN,此时会报错但不中断流程,字段就“残留”了 - 避免在
up()里用Db::insert()填数据——回滚时不会删,造成数据和结构不一致;这类逻辑应交给seed命令单独管理 - 本地调试务必用
php think migrate:rollback --step=1单步回退,验证down()是否真正生效
多环境部署后表结构不一致
核心问题在于:迁移状态存在数据库里(think_migration 表),但迁移文件没同步,或有人绕过迁移手动建表。
- 所有
database/migrations/*.php文件必须提交 Git,严禁只在本地跑完就不管 - 上线前,在各环境执行
SELECT * FROM think_migration ORDER BY id DESC LIMIT 3,比对最后几条记录的version字段(即时间戳)是否一致 - 部署脚本中应明确执行
php think migrate:run --force,避免生产环境交互式确认阻塞流程 - 绝对不要在生产环境用
migrate:reset—— 它会删光所有表,且无法恢复
迁移文件一旦被 run 过,就等同于已发布代码,禁止修改其 up() 或 down() 内容;后续任何调整,都得新建迁移文件增量处理。这点容易被忽略,但恰恰是多人协作不出乱子的关键。



















