Filamentphp Shield多面板权限显示异常及修改后无权限问题
集中式角色权限管理问题及修复方案
问题概述
- 基于Filament Shield插件实现角色权限管理,系统包含admin、home、app三个面板
- 角色列表页面可展示所有面板的114项权限,但在admin面板编辑super_admin角色时,仅能看到admin面板的54项权限,无法管理其他面板的权限
- 尝试修改代码实现集中管理后,出现所有权限均无法显示的问题,修改后的代码如下:
public static function form(Form $form): Form { return $form ->schema([ Forms\Components\Grid::make() ->schema([ Forms\Components\Section::make() ->schema([ Forms\Components\TextInput::make('name') ->label(__('filament-shield::filament-shield.field.name')) ->unique( ignoreRecord: true, modifyRuleUsing: fn (Unique $rule) => ! Utils::isTenancyEnabled() ? $rule : $rule->where(Utils::getTenantModelForeignKey(), Filament::getTenant()?->id) ) ->required() ->maxLength(255), Forms\Components\TextInput::make('guard_name') ->label(__('filament-shield::filament-shield.field.guard_name')) ->default(Utils::getFilamentAuthGuard()) ->nullable() ->maxLength(255), Forms\Components\Select::make(config('permission.column_names.team_foreign_key')) ->label(__('filament-shield::filament-shield.field.team')) ->placeholder(__('filament-shield::filament-shield.field.team.placeholder')) ->default([Filament::getTenant()?->id]) ->options(fn (): Arrayable => Utils::getTenantModel() ? Utils::getTenantModel()::pluck('name', 'id') : collect()) ->hidden(fn (): bool => ! (static::shield()->isCentralApp() && Utils::isTenancyEnabled())) ->dehydrated(fn (): bool => ! (static::shield()->isCentralApp() && Utils::isTenancyEnabled())), ShieldSelectAllToggle::make('select_all') ->onIcon('heroicon-s-shield-check') ->offIcon('heroicon-s-shield-exclamation') ->label(__('filament-shield::filament-shield.field.select_all.name')) ->helperText(fn (): HtmlString => new HtmlString(__('filament-shield::filament-shield.field.select_all.message'))) ->dehydrated(fn (bool $state): bool => $state), ]) ->columns([ 'sm' => 2, 'lg' => 3, ]), ]), Forms\Components\Section::make('Permissions') ->schema([ Tabs::make('Tabs') ->tabs([ self::getPermissionsTab('admin'), self::getPermissionsTab('home'), self::getPermissionsTab('MOJO'), ]), ]), ]); } protected static function getPermissionsTab(string $panel): Tab { $originalPanel = Filament::getCurrentPanel(); Filament::setCurrentPanel(Filament::getPanel($panel)); $permissions = Permission::where('guard_name', $panel)->pluck('name', 'name')->toArray(); Filament::setCurrentPanel($originalPanel); return Tab::make(Str::headline($panel)) ->schema([ CheckboxList::make('permissions') ->label('Permissions from ' . ucfirst($panel)) ->options(array_combine($permissions, $permissions)) ->columns(2), ]); }
代码问题分析
- 面板标识不匹配:第三个Tab传入的面板参数是
MOJO,但系统实际面板为app,导致查询不到对应guard_name的权限 - 字段名冲突:多个Tab的CheckboxList均使用
permissions作为字段名,Filament无法区分不同面板的权限,导致数据绑定异常 - 权限查询逻辑不完善:直接查询Permission模型未利用Shield工具类,可能导致权限列表不完整;冗余的
array_combine操作无意义 - 权限默认值缺失:未设置角色已有权限的默认选中状态,用户无法直观看到当前权限配置
修复方案
1. 修正面板标识
将第三个Tab的面板参数改为app,匹配系统实际面板:
Tabs::make('Tabs') ->tabs([ self::getPermissionsTab('admin'), self::getPermissionsTab('home'), self::getPermissionsTab('app'), // 修正面板名称 ]),
2. 区分权限字段名
为每个面板的权限字段添加前缀,避免冲突,并实现权限合并:
修改getPermissionsTab方法的CheckboxList字段:
CheckboxList::make("permissions_{$panel}") // 添加面板前缀区分字段 ->label('Permissions from ' . ucfirst($panel)) ->options($permissions) ->columns(2) ->default(function ($record) use ($panel) { // 默认选中角色已拥有的对应面板权限 return $record?->permissions->where('guard_name', $panel)->pluck('name')->toArray() ?? []; }),
添加表单数据预处理方法,合并所有面板的权限:
protected function mutateFormDataBeforeSave(array $data): array { $mergedPermissions = []; // 遍历所有面板,合并权限 foreach (['admin', 'home', 'app'] as $panel) { if (isset($data["permissions_{$panel}"])) { $mergedPermissions = array_merge($mergedPermissions, $data["permissions_{$panel}"]); unset($data["permissions_{$panel}"]); } } $data['permissions'] = $mergedPermissions; return $data; }
3. 优化权限查询逻辑
使用Shield的Utils类获取对应面板的完整权限列表:
use BezhanSalleh\FilamentShield\Utils; // ... protected static function getPermissionsTab(string $panel): Tab { $originalPanel = Filament::getCurrentPanel(); $targetPanel = Filament::getPanel($panel); Filament::setCurrentPanel($targetPanel); // 利用Shield工具类获取对应面板的所有权限 $permissions = Utils::getAllPermissions() ->where('guard_name', $targetPanel->getAuthGuard()) ->pluck('name', 'name') ->toArray(); Filament::setCurrentPanel($originalPanel); return Tab::make(Str::headline($panel)) ->schema([ CheckboxList::make("permissions_{$panel}") ->label('Permissions from ' . ucfirst($panel)) ->options($permissions) ->columns(2) ->default(function ($record) use ($targetPanel) { return $record?->permissions->where('guard_name', $targetPanel->getAuthGuard())->pluck('name')->toArray() ?? []; }), ]); }
4. 移除冗余操作
删除array_combine($permissions, $permissions),因为pluck('name','name')已返回键值一致的数组,直接使用$permissions即可。
内容的提问来源于stack exchange,提问作者KrutSednar
相关产品推荐
相关产品推荐

