Laravel User模型添加Multitenantable trait触发无限循环500报错
问题触发原因
无限循环的调用链路非常清晰:
- 你为User模型注册了全局作用域,所有针对User模型的数据库查询,都会自动追加
tenant_id过滤条件 - 全局作用域的逻辑中调用了
Auth::user(),这个方法的底层逻辑是查询User表,匹配当前登录凭证对应的用户记录,返回User模型实例 - 这次用于获取登录用户的User查询,又会触发你注册的tenant_id全局作用域
- 作用域再次调用
Auth::user()尝试获取用户信息,再次触发User查询,形成无限递归,最终栈溢出返回500错误
你测试中删除auth判断段代码后问题消失,正是因为切断了「触发作用域→查询User→触发作用域」的递归链路;而其他非User模型使用原trait不会出问题,是因为其他模型触发全局作用域时,Auth::user()查询的是User模型,不会触发当前模型自身的全局作用域,不会形成递归。
正确实现方案
核心解决思路是:在全局作用域中获取当前用户的租户ID时,避免触发带全局作用域的User查询,切断递归链路。可以直接按如下方式修改trait代码:
trait Multitenantable { public static function bootMultitenantable() { static::addGlobalScope('tenant_id', function (Builder $builder) { $tenant_id = 1; // 直接从认证驱动读取当前登录用户ID,该操作不会查询数据库 $user_id = auth()->id(); if ($user_id) { // 查询租户ID时临时跳过当前全局作用域,避免递归 $tenant_id = static::withoutGlobalScope('tenant_id') ->where('id', $user_id) ->value('tenant_id') ?? 1; } $builder->where('tenant_id', '=', $tenant_id); }); } }
额外说明
- 不要在User模型的全局作用域中直接调用
Auth::user()读取用户模型实例,只要走带全局作用域的User查询就有概率触发递归 - 上述改法和原多租户trait的使用逻辑完全一致,其他模型挂载该trait也不需要额外修改
auth()->id()是直接从session、API token等认证凭证中解析得到用户主键,不需要查询数据库,不会触发额外的模型查询逻辑
内容的提问来源于stack exchange,提问作者Mostafa Lotfi
相关产品推荐
相关产品推荐

