Laravel 12 中通过自定义中间件动态切换子域名租户数据库:解析 $request->getHost() 获取子域名,查 tenants 表验证租户;Config::set 配置连接后必须 DB::purge() 强制重建连接;模型通过重写 newQuery() 动态绑定当前租户连接。

在 Laravel 12 中,中间件根据域名(尤其是子域名)动态切换数据库连接,核心是:在请求进入时解析域名 → 查出对应租户 → 切换数据库配置 → 强制重建连接实例。不能只改配置,必须 purge 并重建,否则连接复用会导致数据错乱。
提取子域名并验证租户
在自定义中间件(如 App\Http\Middleware\InitializeTenant)中,从请求中获取子域名:
- 用
$request->getHost()获取完整域名,再用explode('.', $host, 2)提取第一段作为租户标识(如acme.example.com→acme) - 查
tenants表确认该租户存在且状态为active,不存在则返回 404 或重定向 - 避免直接信任子域名,建议加白名单或哈希校验(如租户 slug 需匹配数据库名规则)
动态设置并重建数据库连接
租户确认后,不能只调 Config::set(),必须配合 DB::purge() 强制释放旧连接:
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 先确保
config/database.php已预定义好连接模板,比如:'mysql_tenant' => [...],实际连接名可拼为mysql_tenant_acme - 执行
Config::set('database.connections.mysql_tenant_acme.database', $tenantDbName) - 立刻调用
DB::purge('mysql_tenant_acme'),否则下次DB::connection('mysql_tenant_acme')还会复用旧连接 - 后续所有
DB::connection('mysql_tenant_acme')查询才真正走新库
让模型自动使用当前租户连接
Eloquent 模型不能靠 protected $connection = 'xxx',它只在类加载时读一次。正确做法是:
- 在中间件里绑定一个解析器到服务容器,例如:
$this->app->bind('tenant.connection', fn() => DB::connection("mysql_tenant_{$tenantSlug}")) - 模型中用
on(app('tenant.connection')->getDatabaseName())不现实;更稳妥的是在基类模型里重写newQuery(),内部调用DB::connection($this->getConnectionName()),而getConnectionName()动态返回当前租户连接名 - 全局作用域 +
tenant_id字段仍需保留,作为双重隔离(连接级 + 查询级)
路由层配合子域名约束
在 RouteServiceProvider 中用 Route::domain('{tenant}.example.com') 注册租户专属路由组,这样 $request->route('tenant') 就能直接拿到租户标识,无需手动解析 Host:
- 注意 domain 路由需配合 Web 服务器(Nginx/Apache)泛解析,且 HTTPS 证书要支持通配符
- 不要把租户逻辑塞进闭包路由,统一收口到中间件,便于日志、缓存、异常处理
- 主域名(如 example.com)应跳转或显示登录页,不参与租户逻辑

















