Laravel 5多租户独立数据库:适配租户连接至所有DB原生查询
适配多租户架构下的Laravel原生数据库查询
嘿,看起来你已经把多租户的基础配置搞定了——公共库处理认证、租户独立库、中间件和Model的连接配置都跑通了,很棒!针对原生DB::raw、DB::table这类查询适配租户连接的问题,我给你几个实用的方案,按需选就行:
方案1:直接指定租户连接(最直观)
Laravel的DB门面支持通过connection()方法指定要使用的连接名,刚好你已经在config/database.php里配置了tenant_mysql连接,直接用它就行:
针对DB::table
// 直接指定租户连接查询表 $tenantOrders = DB::connection('tenant_mysql')->table('orders')->where('status', 'completed')->get();
针对DB::raw
不管是配合select()还是其他方法,只要先指定租户连接,再用DB::raw()就会自动关联到租户库:
// 结合DB::raw统计数据 $monthlyRevenue = DB::connection('tenant_mysql') ->select(DB::raw('SUM(amount) as total FROM orders WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)'));
这个方案适合零散的原生查询,简单直接,不用额外封装。
方案2:封装辅助函数(更优雅,减少重复代码)
如果项目里原生查询很多,每次写DB::connection('tenant_mysql')会很啰嗦,咱们可以封装一个全局辅助函数来简化:
- 在项目根目录创建
app/helpers.php文件,添加以下代码:
if (!function_exists('tenantDB')) { /** * 返回租户数据库连接的查询构建器 */ function tenantDB() { return DB::connection('tenant_mysql'); } }
- 记得在
composer.json里自动加载这个文件(如果没加的话):
"autoload": { "files": [ "app/helpers.php" ], // 其他自动加载配置... }
然后执行composer dump-autoload让配置生效。
之后用原生查询就可以这样写:
// 替代DB::connection('tenant_mysql')->table() tenantDB()->table('users')->where('email', 'user@example.com')->first(); // 结合DB::raw使用 tenantDB()->select(DB::raw('SELECT COUNT(*) FROM products WHERE stock > 0'));
以后如果租户连接名需要修改,只改辅助函数里的字符串就行,不用全局替换,维护起来更方便。
方案3:中间件自动切换默认连接(全局生效)
如果你希望所有原生查询默认都走租户连接,只有公共库的查询手动指定公共连接,可以在你的MultiTenant中间件里动态修改默认数据库配置:
public function handle(Request $request, Closure $next) { // 这里假设你已经通过请求、session或其他方式获取到当前租户的数据库名 $tenantDbName = $this->getCurrentTenantDatabaseName(); if ($tenantDbName) { // 更新租户连接的数据库名(每个租户独立库的关键) config(['database.connections.tenant_mysql.database' => $tenantDbName]); // 清除旧连接缓存,避免复用之前的连接 DB::purge('tenant_mysql'); // 重新连接租户库 DB::connection('tenant_mysql')->reconnect(); // 设置默认数据库连接为租户连接 config(['database.default' => 'tenant_mysql']); } return $next($request); }
这样配置后,DB::table()、DB::raw()默认就会使用租户连接,而需要访问公共认证库时,手动指定公共连接即可:
// 访问公共认证库的用户表 $publicUser = DB::connection('mysql')->table('users')->where('id', 1)->first();
⚠️ 注意:这个方案要小心全局副作用,比如队列任务、定时任务里如果没有租户上下文,可能会连接错误的库,所以在非HTTP请求的场景下要额外处理。
关键注意事项
- 动态更新租户库名:如果每个租户是独立数据库(而不是同一库的不同表),一定要在中间件里动态设置
tenant_mysql连接的database参数,并且执行purge()和reconnect(),否则会一直复用第一个租户的连接,导致串库。 - 验证连接正确性:测试时可以用
DB::connection('tenant_mysql')->getDatabaseName()来确认当前连接的数据库名是否是预期的租户库。
内容的提问来源于stack exchange,提问作者Sebastien D
相关产品推荐
相关产品推荐

