Filament多租户与Spatie Permissions集成问题:组织资源数据不显示及深层关联租户配置咨询
Filament多租户与Spatie Permissions集成问题:组织资源数据不显示及深层关联租户配置咨询
嗨,我来帮你梳理下这个Filament多租户和Spatie权限集成的问题,先从你最核心的「组织资源数据不显示」问题说起,再拓展到深层关联模型的租户配置:
一、组织资源数据不显示的核心原因与修复方案
你设置了protected static ?string $tenantOwnershipRelationshipName = 'users';,但这个属性的作用是让Filament识别租户模型(Organization)关联到用户的关系,可你当前的多对多关系下,Filament默认的租户范围逻辑没办法直接适配,导致数据筛选失效。
1. 修正OrganizationResource的查询逻辑
直接在OrganizationResource的getEloquentQuery方法里手动添加用户关联的筛选,同时区分管理员和普通用户的权限:
public static function getEloquentQuery(): Builder { $query = parent::getEloquentQuery() ->withoutGlobalScopes([ SoftDeletingScope::class, ]); // 非管理员用户只能看到自己关联的组织 $user = auth()->user(); if ($user && !$user->hasRole('super_admin') && !$user->organizations()->where('is_admin', true)->exists()) { $query->whereHas('users', function ($q) use ($user) { $q->where('user_id', $user->id); }); } return $query; }
2. 验证租户中间件的生效逻辑
你在AdminPanelProvider里加了ApplyTenantScopes::class中间件,但这个中间件默认适配的是用户与租户一对多的关系(比如用户属于单个组织),对于多对多场景需要自定义租户范围:
创建一个全局范围类App\Scopes\OrganizationTenantScope:
<?php namespace App\Scopes; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; use Illuminate\Support\Facades\Auth; use Filament\Facades\Filament; class OrganizationTenantScope implements Scope { public function apply(Builder $builder, Model $model) { $user = Auth::user(); if (!$user) return; // 管理员或属于超级组织的用户跳过筛选 if ($user->hasRole('super_admin') || $user->organizations()->where('is_admin', true)->exists()) { return; } $currentTenant = Filament::getTenant(); if (!$currentTenant) return; // 针对不同模型适配筛选逻辑 if ($model instanceof \App\Models\Organization) { $builder->whereHas('users', fn($q) => $q->where('user_id', $user->id)); } elseif (method_exists($model, 'user')) { // 比如Game模型,关联到User再关联到Organization $builder->whereHas('user.organizations', fn($q) => $q->where('id', $currentTenant->id)); } elseif (method_exists($model, 'game')) { // 比如Media模型,关联到Game -> User -> Organization $builder->whereHas('game.user.organizations', fn($q) => $q->where('id', $currentTenant->id)); } } }
然后在Organization模型的booted方法里注册这个全局范围:
protected static function booted() { parent::booted(); static::addGlobalScope(new \App\Scopes\OrganizationTenantScope()); // 保留你原来的created逻辑 self::created(function ($model) { $session_team_id = config('organizations.default_organization_id'); setPermissionsTeamId($model); setPermissionsTeamId($session_team_id); }); }
二、深层关联模型(Media -> Game -> User -> Organizations)的租户配置
对于这类嵌套关联的模型,你可以通过两种方式处理:
- 全局范围统一处理:上面的
OrganizationTenantScope已经包含了对Game、Media这类模型的筛选逻辑,只要模型上有对应的关联方法(比如game()、user()),就能自动应用租户范围。 - 资源单独配置:如果需要更精细的控制,也可以在对应资源的
getEloquentQuery里手动添加筛选:
比如MediaResource:
public static function getEloquentQuery(): Builder { $query = parent::getEloquentQuery(); $user = auth()->user(); if ($user && !$user->hasRole('super_admin') && !$user->organizations()->where('is_admin', true)->exists()) { $currentTenant = Filament::getTenant(); $query->whereHas('game.user.organizations', fn($q) => $q->where('id', $currentTenant->id)); } return $query; }
三、额外注意点
- 确保用户切换租户后,Filament能正确获取到当前租户:可以在用户切换租户的逻辑里,把租户ID存入session,方便全局范围调用。
- 测试管理员权限:当用户属于
is_admin = true的组织时,要确保能绕过所有租户范围,看到所有数据。
备注:内容来源于stack exchange,提问作者Kristian Vasilev
相关产品推荐
相关产品推荐

