ThinkPHP二级域名绑定模型的核心是通过请求上下文识别子域名并动态切换数据源,模型自动适配;多应用下数据隔离为默认前提,共享需主动设计。

ThinkPHP 配置二级域名绑定模型,核心不是让“模型自己认域名”,而是通过请求上下文识别子域名 → 动态切换数据源或作用域 → 模型自动适配。多应用下数据隔离是默认前提,共享则需主动设计,不能靠框架自动解决。
一、先确保多应用结构和域名路由已就位
没走通基础绑定,模型层无从谈起:
- 确认已启用多应用:执行
composer require topthink/think-multi-app,删除根目录app/controller,用php think build admin等命令生成独立应用目录(如app/admin/、app/api/) - 开启域名识别:
config/app.php中设'url_domain_deploy' => true(注意不是sub_domain_deploy,后者在 TP6+ 已不推荐) - 注册子域名路由:
route/app.php中写明绑定关系,例如:Route::domain('admin', 'admin');Route::domain('api', 'api');
这样admin.example.com就会加载app/admin/下的全部逻辑,包括其专属模型与数据库配置
二、数据隔离:按应用物理分离是最简方案
每个应用默认使用自己的配置文件,天然实现隔离:
-
app/admin/config/database.php可配置独立数据库连接(如db_admin),app/api/config/database.php配另一套(如db_api) - 模型放在对应应用目录下,例如
app/admin/model/User.php,自动读取app/admin/config/database.php,无需手动指定连接 - 若共用同一数据库但需表前缀区分,可在各应用
database.php中设置不同'prefix',如'admin_'和'api_'
三、有限共享:跨应用调用模型要明确路径与连接
共享不等于混用。常见合理场景有两类:
立即学习“PHP免费学习笔记(深入)”;
-
只读共享基础数据:比如用户中心在
app/index,后台admin.example.com需查用户昵称。可在app/admin/model下新建UserRead.php,显式指定连接:protected $connection = 'index'; // 对应 config/database.php 中定义的连接名 -
统一模型 + 租户上下文:SaaS 场景下所有子域名共用一套模型,但数据按租户隔离。此时不靠应用目录分隔,而靠运行时动态切换:
在中间件中解析子域名 → 生成租户标识(如tenant1)→ 调用Db::setConnectConfig()切换库名与前缀 → 执行Db::clearConnectionPool()清空旧连接
四、模型层强制过滤:避免“查到别人的数据”
即使连对了库,User::find(1) 仍可能返回非本租户数据——因为 ThinkPHP 模型本身不带租户意识:
- 所有业务模型必须定义全局作用域,例如:
protected static function boot(): void { self::addGlobalScope('tenant', function (Builder $builder) { $tenant = request()->get('tenant_id') ?: 'default'; $builder->where('tenant_id', $tenant); }); } - 禁用
Db::table()直接操作,防止绕过模型作用域;统一用User::where(...)->find()等 Eloquent 式调用 - 控制器中不要硬编码
tenant_id,从请求上下文(如子域名、Header 或 Token)中提取并注入



















