Create Custom Page in Filament: Role Permission Management Page
with Filament Admin Panel you can create custom pages however you want.
here I am going to create a Role-Permission management page. it looks like this:
Create a view and a class file for the page
with this artisan command start creating the page
php artisan make:filament-page ManageRolePermissions --type=custom
You will have a class named ManageRolePermissions.php
in app\Filament\Pages
and a view file manage-role-permissions.blade.php
in resources\views\filament\pages
a sidebar item is created and the page is empty.
using laravel-permissions package.
I am using spatie/laravel/permission package becasue makes it easy for us to assign roles and permissions.
composer require spatie/laravel-permission
publish and run the migrations
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan optimize:clear
php artisan migrate
now the database table is ready.
we need to seed some default roles and permissions to work with.
so let’s create a RoleSeeder and a PermissionSeeder.
php artisan make:seeder RoleSeeder
php artisan make:seeder PermissionSeeder
//RoleSeeder.php
use Spatie\Permission\Models\Role;
class RoleSeeder extends Seeder
{
public function run()
{
$roles = ['admin', 'author', 'editor', 'moderator'];
foreach ($roles as $role) {
Role::create(['name' => 'admin',]);
}
}
}
//PermissionSeeder.php
use Spatie\Permission\Models\Permission;
class PermissionSeeder extends Seeder
{
public function run()
{
$permissions = ['create post', 'edit post', 'delete post', 'create user', 'delete user', 'delete comment'];
foreach ($permissions as $permission) {
Permission::create(['name' => $permission]);
}
}
}
then run the seeders:
php artisan db:seed --class=RoleSeeder
php artisan db:seed --class=PermissionSeeder
Preparing the UI
first, in the Page Class we have to get all roles and permissions to show them in the view page:
// ManageRolePermissions.php
public $roles;
public $permissions;
public function mount()
{
$this->roles = Role::all();
$this->permissions = Permission::all()->pluck('name')->toArray();
}
$roles
and $permissions
are for showing roles and permissions inside the page.
Filament pages are just like livewire components. so in the class we can use mount()
method to set an initial value for the properties.
On this page, I want to have two boxes. in one box there is a list of permissions and in another one list of roles.
<x-filament::page>
<div class="grid grid-cols-2 " >
<div class="mr-6">
<div class="text-2xl mb-6 font-bold">Roles</div>
<div class="bg-white border-2 rounded-lg border-primary-500 p-1.5 w-full ">
<ul>
<select multiple class="w-full border-0">
@foreach($roles as $role)
<option >
{{$role->name}}
</option>
@endforeach
</select>
</ul>
</div>
</div>
<div>
<div class="text-2xl mb-6 font-bold">Roles</div>
<div class=" bg-white border-2 rounded-lg border-primary-500 p-1.5 w-full ">
<ul >
@foreach($permissions as $permission)
<li class="mb-6"><span>
<input type="checkbox"
value="{{$permission}}"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
>
</span>
{{$permission}}
</li>
@endforeach
</ul>
</div>
</div>
</div>
<div>
<button
class="text-white bg-danger-500 focus:ring-4 focus:ring-yellow-300 font-medium rounded-lg text-lg px-6 py-4">
Save
</button>
</div>
</x-filament::page>
here we created two columns one for listing roles and another for listing permissions.
in the first column, roles are shown in a <select>
element. the multiple
attribute makes it a box in which we can select each role without a dropdown.
the second column shows permissions in a list. every permission has a checkbox we can use to select and assign the permission to a role.
at the bottom of the page, there is a button to save the permissions we assigned to each role.
Here is the UI:
now we have to implement:
- assigning permissions to a role
- showing each role’s permissions dynamically.
Assign Permissions to a Role
we want to click on a role then select permissions and finally click on the save button to assign permissions.
so first we add two properties to the class.
public $selectedRole;
public $selectedPermissions;
public function mount()
{
$this->roles = Role::all();
$this->permissions = Permission::all()->pluck('name')->toArray();
$this->selectedPermissions = [];
}
because $selectedPermissions
is an array we set its initial value inside the mount
method.
the role that we click on, will be in $selectedRole
and permissions will be in $selectedPermissions
.
we have to change the view code like this:
in the Roles section.
<select wire:model="selectedRole" multiple class="w-full border-0">
@foreach($roles as $role)
<option>
{{$role->name}}
</option>
@endforeach
</select>
we used wire:model
attribute to bind the selected role value to $selectedRole
property.
and for the permissions:
<ul>
@foreach($permissions as $permission)
<li class="mb-6"><span>
<input
value="{{$permission}}"
wire:model="selectedPermissions"
type="checkbox"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
>
</span>
{{$permission}}
</li>
@endforeach
</ul>
just like the roles section, we use wire:model="selectedPermissions"
to put selected permissions in an array.
now we have the role and the permissions. we need to do the permission assignment.
for that we use livewire actions. define an action on the save button. like so:
<button wire:click="saveRolePermissions"
class="text-white bg-danger-500 focus:ring-4 focus:ring-yellow-300 font-medium rounded-lg text-lg px-6 py-4">
Save
</button>
wire:click="saveRolePermissions"
means when you click on this element a method called saveRolePermissions
will run in the component class.
therefore we need to define this function in the RolePermissionManagemen
class:
public function saveRolePermissions()
{
$selectedRoleModel = Role::where('name', $this->selectedRole)->first();
$selectedRoleModel->syncPermissions($this->selectedPermissions);
}
since we installed and used spatie/laravel-permission
package it is very easy to assign an array of permissions to a role using syncPermissions
method.
now if you click on a role, select permissions, and click on the save button the selected permissions will be assigned to the role.
we managed to assign permissions to a role.
if we refresh the page and click on a role no item is checked in thw Permissions box.
for this to work we need to add an action to the role so that by clicking on it, the related permissions in the permissions box show up.
Showing Each Role’s Permissions Dynamically
the Roles section code will change like this:
<ul>
<select wire:model="selectedRole" multiple class="w-full border-0">
@foreach($roles as $role)
<option wire:click="showPermissions('{{$role->name}}')">
{{$role->name}}
</option>
@endforeach
</select>
</ul>
we only added wire:click
attribute to run an action and inside the action we passed the role’s name.
finally, we define the action in the class:
public function showPermissions($roleName)
{
$this->selectedRole = $roleName;
$this->selectedPermissions = Role::where('name', $roleName)->first()->permissions->pluck('name')->toArray();
}
in this method first, we set the $selectedRole
property. then we get the role’s permissions as an array to set the $selectedPermissions
value.
check the page again we can see the related permissions for every role by clicking on them.
Full Code of View File and Page Class
view file:
<x-filament::page>
<div class="grid grid-cols-2 " >
<div class="mr-6">
<div class="text-2xl mb-6 font-bold">Roles</div>
<div class=" bg-white border-2 rounded-lg border-primary-500 p-1.5 w-full ">
<ul>
<select wire:model="selectedRole" multiple class="w-full border-0">
@foreach($roles as $role)
<option wire:click="showPermissions('{{$role->name}}')">
{{$role->name}}
</option>
@endforeach
</select>
</ul>
</div>
</div>
<div>
<div class="text-2xl mb-6 font-bold">Permissions</div>
<div class=" bg-white border-2 rounded-lg border-primary-500 p-1.5 w-full ">
<ul >
@foreach($permissions as $permission)
<li class="mb-6"><span>
<input
value="{{$permission}}" wire:model="selectedPermissions"
type="checkbox"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
>
</span>
{{$permission}}
</li>
@endforeach
</ul>
</div>
</div>
</div>
<div>
<button wire:click="saveRolePermissions"
class="text-white bg-danger-500 focus:ring-4 focus:ring-yellow-300 font-medium rounded-lg text-lg px-6 py-4">
Save
</button>
</div>
</x-filament::page>
class:
<?php
namespace App\Filament\Pages;
use Filament\Pages\Page;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
class ManageRolePermissions extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-document-text';
protected static string $view = 'filament.pages.manage-role-permissions';
public $roles;
public $permissions;
public $selectedRole;
public $selectedPermissions;
public function mount()
{
$this->roles = Role::all();
$this->permissions = Permission::all()->pluck('name')->toArray();
$this->selectedPermissions = [];
}
public function saveRolePermissions()
{
$selectedRoleModel = Role::where('name', $this->selectedRole)->first();
$selectedRoleModel->syncPermissions($this->selectedPermissions);
}
public function showPermissions($roleName)
{
$this->selectedRole = $roleName; // Set the selectedRole
$this->selectedPermissions = Role::where('name', $roleName)->first()->permissions->pluck('name')->toArray();
}
}