Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer collective, backed by the Pireon OAuth identity server. The public site is a separate, server-rendered application consuming /api/public/v1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
101 lines
2.8 KiB
PHP
101 lines
2.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Filament\Resources\Roles\Pages;
|
|
|
|
use App\Support\Permissions;
|
|
use Spatie\Permission\PermissionRegistrar;
|
|
|
|
/**
|
|
* Bridges the form's per-group checkbox state and the role's single
|
|
* `permissions` relationship.
|
|
*
|
|
* The form splits permissions across one component per resource for
|
|
* readability; the database wants one flat set. This trait converts in both
|
|
* directions and keeps the two in step.
|
|
*/
|
|
trait HandlesRolePermissions
|
|
{
|
|
/**
|
|
* Permission names captured on the way to saving, held here because the
|
|
* relationship can only be synced once the role row exists.
|
|
*
|
|
* @var array<int, string>
|
|
*/
|
|
protected array $selectedPermissions = [];
|
|
|
|
/**
|
|
* Expand the role's flat permission set into the form's grouped shape.
|
|
*
|
|
* @param array<string, mixed> $data
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function mutateFormDataBeforeFill(array $data): array
|
|
{
|
|
$held = $this->getRecord()->permissions->pluck('name')->all();
|
|
|
|
foreach (Permissions::grouped() as $group => $permissions) {
|
|
$data['permission_groups'][$group] = array_values(array_intersect($permissions, $held));
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function mutateFormDataBeforeSave(array $data): array
|
|
{
|
|
return $this->extractPermissions($data);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function mutateFormDataBeforeCreate(array $data): array
|
|
{
|
|
return $this->extractPermissions($data);
|
|
}
|
|
|
|
protected function afterSave(): void
|
|
{
|
|
$this->syncSelectedPermissions();
|
|
}
|
|
|
|
protected function afterCreate(): void
|
|
{
|
|
$this->syncSelectedPermissions();
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function extractPermissions(array $data): array
|
|
{
|
|
$groups = $data['permission_groups'] ?? [];
|
|
unset($data['permission_groups']);
|
|
|
|
// Intersecting with the catalogue means a stale or hand-crafted value
|
|
// in the payload cannot grant a permission the app does not define.
|
|
$this->selectedPermissions = array_values(array_intersect(
|
|
array_merge(...array_values(array_map(fn ($v) => (array) $v, $groups)) ?: [[]]),
|
|
Permissions::all(),
|
|
));
|
|
|
|
return $data;
|
|
}
|
|
|
|
protected function syncSelectedPermissions(): void
|
|
{
|
|
$this->getRecord()->syncPermissions($this->selectedPermissions);
|
|
|
|
// spatie caches the permission map; without this the change would not
|
|
// take effect until the cache expired.
|
|
app(PermissionRegistrar::class)->forgetCachedPermissions();
|
|
}
|
|
}
|