ThinkPHP Migration 跑到远端库时 Unknown database 的真相
某天需要给一张放在远程 MySQL 的业务表追加唯一索引,迁移文件写完,php think migrate:run 一跑:
PDOException: SQLSTATE[42000]: Syntax error or access violation:
1049 Unknown database 'remote_biz'
看着这行报错我愣了一下——config/database.php 里明明配了这个连接,为什么 Migration 说不认识?
现象
项目里有两个 MySQL 连接:默认连接指向本机 127.0.0.1,另一个命名连接 remote_biz 指向远端服务器 x.x.x.x。迁移文件用了跨库语法:
$this->execute("ALTER TABLE `remote_biz`.`orders` ADD UNIQUE INDEX `uk_no` (`order_no`)");报错指向 1049 Unknown database 'remote_biz',但连接配置就在那里,没有任何拼写错误。
根因
ThinkPHP 8.x 的 Migration 底层是 Phinx。Phinx 有自己独立的数据库适配器,它只认 config/database.php 里的默认连接(default 键指向的那一条)。
$this->execute()、$this->table()、$this->query() 这些 Phinx 方法全部走这个默认适配器,完全绕过了 ThinkPHP 的多连接路由机制。
当你在 SQL 里写 remote_biz.orders,Phinx 会把这条 SQL 原样发给本地 MySQL 去执行。本地 MySQL 上根本没有 remote_biz 这个数据库——它在另一台服务器上——于是报 1049 Unknown database。
remote_biz 是 ThinkPHP 的连接别名,不是本地 MySQL 实例里的数据库名。跨服务器的跨库语法 db_name.table_name 只在同一 MySQL 实例内有效,跨主机完全无法工作。Phinx 适配器与 ThinkPHP Db 门面是两套独立的连接池,互不感知。
正解
在迁移文件里直接用 think\facade\Db::connect() 拿到命名连接,再在这个连接上执行 SQL:
<?php
use think\migration\Migrator;
use think\facade\Db;
class AddUniqueIndexToOrders extends Migrator
{
public function up(): void
{
// 先清理存量重复数据,否则加唯一索引会失败
$duplicates = Db::connect('remote_biz')->query(
"SELECT order_no, COUNT(*) as cnt
FROM orders
GROUP BY order_no
HAVING cnt > 1"
);
foreach ($duplicates as $dup) {
Db::connect('remote_biz')->execute(
"DELETE FROM orders
WHERE order_no = ? AND id NOT IN
(SELECT id FROM (SELECT MAX(id) AS id FROM orders WHERE order_no = ?) t)",
[$dup['order_no'], $dup['order_no']]
);
}
// 在远端连接上执行 DDL,不加库名前缀
Db::connect('remote_biz')->execute(
"ALTER TABLE `orders` ADD UNIQUE INDEX `uk_order_no` (`order_no`)"
);
}
public function down(): void
{
Db::connect('remote_biz')->execute(
"ALTER TABLE `orders` DROP INDEX `uk_order_no`"
);
}
}关键点:SQL 里不再写 remote_biz.orders,只写表名 orders,因为连接本身已经路由到正确的服务器和数据库,不需要再加库名前缀。
非默认连接的所有 DDL/DML 都走 Db::connect('connection_name')->execute()。Migration 的元数据(哪些迁移跑过)仍然写入默认库的 migrations 表,回滚追踪不受影响。$this->table() 同样受限,只要目标不是默认连接,一律换成 Db::connect()。
验证
php think migrate:run
# == 20260315120000 AddUniqueIndexToOrders: migrated (took 0.18s)连到远端 MySQL 确认:
SHOW INDEX FROM orders WHERE Key_name = 'uk_order_no';
-- 能查到说明索引已正确建在远端库上一句话外卖
Phinx 的所有
$this->*方法只走默认连接,非默认连接的 ThinkPHP Migration 必须改用Db::connect('name')手动路由。