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

Filament多租户环境下ApplyTenantScopes中间件重复执行引发查询重复问题

Filament多租户:ApplyTenantScopes中间件重复执行导致SQL条件重复

在Filament多租户场景中,ApplyTenantScopes中间件的handle方法会在每个页面请求时执行两次,最终生成的SQL查询包含重复的租户过滤条件,例如:

select * from "categories" where "categories"."unit_id" in (1) and "categories"."unit_id" in (1) order by "categories"."id" asc

不确定这是框架Bug还是自身配置错误,相关代码如下:

中间件代码

class ApplyTenantScopes
{
    /**
     * Handle an incoming request.
     *
     * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        Category::addGlobalScope(
            fn(Builder $query) => $query->whereBelongsTo(Filament::getTenant()),
        );

        return $next($request);
    }
}

面板提供者注册代码

->tenantMiddleware([
    ApplyTenantScopes::class,
], isPersistent: true)

问题原因与解决方案

核心原因

  1. 全局作用域重复添加:全局作用域是绑定在模型类上的持久化设置,每次中间件执行都会给Category新增一次相同的作用域,两次执行就会生成重复的SQL条件。
  2. 持久化中间件的触发逻辑:设置isPersistent: true后,中间件会作用于所有应用路由(包括Filament内部的AJAX请求、资源加载请求等),同一请求链路中可能被多次触发。

修复方案

方案1:避免重复添加全局作用域

在添加作用域前先检查是否已存在,防止重复绑定:

public function handle(Request $request, Closure $next): Response
{
    // 通过作用域名称判断是否已添加
    if (!collect(Category::getGlobalScopes())->has('tenant_filter')) {
        Category::addGlobalScope('tenant_filter', function (Builder $query) {
            $query->whereBelongsTo(Filament::getTenant());
        });
    }

    return $next($request);
}

方案2:调整中间件注册方式

如果不需要中间件在所有路由生效,移除isPersistent: true,仅让其作用于Filament面板路由:

->tenantMiddleware([
    ApplyTenantScopes::class,
])

方案3:改用Filament内置租户机制

Filament原生支持租户模型过滤,无需手动编写中间件:
让Category模型实现HasTenants接口并定义租户关联:

use Filament\Models\Contracts\HasTenants;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Category extends Model implements HasTenants
{
    public function getTenant(): ?Model
    {
        return Filament::getTenant();
    }

    public function tenant(): BelongsTo
    {
        return $this->belongsTo(Unit::class, 'unit_id');
    }
}

Filament会自动为该模型添加租户过滤逻辑,无需手动维护中间件。

内容的提问来源于stack exchange,提问作者marin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 13:53:19