You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 03:45:45