This site uses tracking cookies used for marketing and statistics. Privacy Policy

Step-by-step guide for using Laravel-permissions package from spatie

September 16th, 2022

What is Laravel-permission?

Not every user needs to have an access to all the data in the database. Let’s take the example of a college website. The access to the data and permissions allowed to a teacher will be different from that of a student. Why? Because their roles and responsibilities are different.

Laravel-permission allows you to do the same with your database. It lets you manage users’ roles and permissions in your database. Let’s see how you can do the same step-by-step.

Prefer watching a video instead? No worries! Just follow along with the video below.

Step 1. Install Laravel project

To keep things simple, I am going to show you how to install Laravel using composer provided that you already have WAMP or XAMPP installation and Composer on your computer.

composer create-project laravel/laravel laravel-permission-demo
cd laravel-permission-demo

Once the application is started after you run the above-mentioned command, you have to start Laravel’s local development server. For that, use Artisan CLI’s serve command.

php artisan serve

Now you have to create a new database in PhpMyAdmin and add the database details in the root directory .env file. We are going to use MySQL.

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel_permission_demo
DB_USERNAME=root
DB_PASSWORD=password

Step 2. Install Breeze

What is Breeze?

Whenever you go to any website, you will often see login and signup forms which also reset and confirm passwords, and verify your email. Lararvel Breeze helps you do exactly this with your website in a simple way.

Now, if you want to assign permissions to your users, you need to have a proper login system. So here is how you can install Breeze and publish the authentication views, routes, controllers, and other resources.

composer require laravel/breeze --dev
php artisan breeze:install

Now it’s time to compile the frontend assets of your application. For a new terminal, run: -

npm install
npm run dev

For database migration: -

php artisan migrate

Once your migration users and tables are created in your database, you can see the following.

Now you can check your application’s login and register URLs

For login

http://127.0.0.1:8000/login

For register

http://127.0.0.1:8000/register

Step 3. Install Laravel-permission package

Why do we use Laravel permission?

Not everybody needs to get access to everything in your database. Otherwise, you may be running the risk of jeopardizing your invaluable data. Thus, Laravel permissions give you the power to limit access to data as per the roles of the user. So next we will cover how to install the Laravel-permission package.

I would recommend you to first go and check the prerequisites page for user models. Please also check that you don’t have a file named config/permission.php because this package will publish a file with that name. If you have one, then rename it.

Run this command.

composer require spatie/laravel-permission

You can even manually add the service provider in your config/app.php file. However, it is optional.

'providers' => [
    // ...
    Spatie\Permission\PermissionServiceProvider::class,
];

Now publish the migration and the config/permission.php config file using: -

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

Here I want to point out a couple of things: -

  1. If you want to use teams feature, updates your config/permission.php file and set  'teams' => true. If you want to use a custom foreign key for teams, you should also change teamforeignkey. I also recommend you to check the advanced section of docs on UUID steps if your are using UUIDs.

  2. Clear your config cache as it is a bad practice to do config-caching while developing. For clearing caching configurations locally, use these commands: -

php artisan optimize:clear
 # or
php artisan config:clear

Once the config and migration have been published and configured, create the tables for this package by running: -

php artisan migrate

Add all the required traits to your user model. You can refer to the Basic Usage section of the docs to know how to get started using the feature of this package.

App\Models\User.php

Add this line user.php

use Spatie\Permission\Traits\HasRoles;

After that user HasRoles in class.

- use HasApiTokens, HasFactory, Notifiable;
+ use HasApiTokens, HasFactory, Notifiable , HasRoles;

Step 4: Create permission, roles, and screens

What is a role?

The role is the authority we assign to someone for access to the data. The higher someone’s role is, the more permissions he will get. Usually, we assign roles as per the rank of the user. For example - executives may get the role of super admin to do anything he wants. Here is how you can create permissions, roles, and screens.

First, we will create a database seeder for the super admin user.

php artisan make:seeder BasicPermissionSeeder

Use spatie classes in seeder file.

use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Spatie\Permission\PermissionRegistrar;

Now, replace this code in database\seeder\BasicPermissionSeeder.php

<?php

namespace Database\Seeders;

// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Spatie\Permission\PermissionRegistrar;

class BasicPermissionSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // Reset cached roles and permissions
        app()[PermissionRegistrar::class]->forgetCachedPermissions();

        // create permissions
        $permissions = [
            'permission list',
            'permission create',
            'permission edit',
            'permission delete',
            'role list',
            'role create',
            'role edit',
            'role delete',
            'user list',
            'user create',
            'user edit',
            'user delete'
         ];
        foreach ($permissions as $permission) {
            Permission::create(['name' => $permission]);
        }
        // create roles and assign existing permissions
        $role1 = Role::create(['name' => 'writer']);
        $role1->givePermissionTo('permission list');
        $role1->givePermissionTo('role list');
        $role1->givePermissionTo('user list');
        $role2 = Role::create(['name' => 'admin']);
        
        foreach ($permissions as $permission) {
            $role2->givePermissionTo($permission);
        }
        $role3 = Role::create(['name' => 'super-admin']);
        // gets all permissions via Gate::before rule; see AuthServiceProvider
        // create demo users
        $user = \App\Models\User::factory()->create([
            'name' => 'Super Admin',
            'email' => 'superadmin@example.com',
        ]);
        $user->assignRole($role3);
        $user = \App\Models\User::factory()->create([
            'name' => 'Admin User',
            'email' => 'admin@example.com',
        ]);
        $user->assignRole($role2);
        $user = \App\Models\User::factory()->create([
            'name' => 'Example User',
            'email' => 'test@example.com',
        ]);
        $user->assignRole($role1);
    }
}

So, just to recap, till now we have created a super admin user, a test user, and an admin user and assigned to each one of them permissions as per role 1, role 2, and role 3 respectively.

Next, you have to run the database seed command to insert data into the database.

php artisan db:seed --class=BasicPermissionSeeder

Grant Super-Admin access

Gate has allowed all the permissions to the super-admin. We are using Laravel’s default password for the super-admin, which is “password”.

Add below a Gate::before checking in your AuthServiceProviderboot function.

use Illuminate\Support\Facades\Gate;
// Implicitly grant "Super-Admin" role all permission checks using can()
Gate::before(function ($user, $ability) {
    if ($user->hasRole(env('APP_SUPER_ADMIN', 'super-admin'))) {
        return true;
    }
});

Add permission check

Now all the users have full access to the application as we have not added permission to check Laravel’s default can function.

$user->can('permission create');

In Blade directives:

@can('permission create')
...
@endcan

Next, we are going to implement CRUD (screen) for the permission and roles.

Step 5: Permission management CRUD

Follow these steps to create permission CRUD for our Laravel Admin panel.

For the permission

Step 1: Create a model

We are going to start by creating a model for permission CRUD. You can create your model using the make:model Artisan command. It will create a Permission.php file in app/Models folder.

We can also create the models and controllers manually.

php artisan make:model Permission

Next is to extend our permission model with Spatie\\Permission\\Models\\Permission and update the model with the below code.

app/Models/Permission.php

<?php

namespace App\Models;
use Spatie\Permission\Models\Permission as OriginalPermission;

class Permission extends OriginalPermission
{
    protected $fillable = [
        'name',
        'guard_name',
        'updated_at',
        'created_at'
    ];
}

Step 2: Create a controller

The make:controller Artisan command is used to create the controllers.

php artisan make:controller Admin/PermissionController --model=Permission --resource

The --resource option is used to quickly create a controller to handle create, read, update, and delete (“CRUD”) operations.

Step 3: Add routes

Extend controllers in web.php file.

use App\\Http\\Controllers\\Admin\\PermissionController;

Add resource route in web.php. We are using auth middleware and Admin namespace.

Route::prefix('admin')->namespace('App\\Http\\Controllers\\Admin')->middleware(['auth'])->group(function() {
    Route::resource('permission', PermissionController::class);
});

Step 4: Add a link to the navigation

So, we have completed the creation of CRUD for permission. Now add the permission link below the Navigation Links on navigation.blade.php.

resources/views/layouts/navigation.blade.php

<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
     <x-nav-link :href="route('dashboard')" 
		:active="request()->routeIs('dashboard')">
		{{ __('Dashboard') }}
	</x-nav-link>
	<x-nav-link :href="route('permission.index')" 
		:active="request()->routeIs('permission.index')">
		{{ __('Permission') }}
	</x-nav-link>
</div>

For mobile navigation

<!-- Responsive Navigation Menu -->
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
	<div class="pt-2 pb-3 space-y-1">
	<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">			       {{ __('Dashboard') }}
	</x-responsive-nav-link>
	<x-responsive-nav-link :href="route('permission.index')" :active="request()->routeIs('permission.index')">
		{{ __('Permission') }}
	</x-responsive-nav-link>
</div>

Step 5: Index page with action links

Update index function with permission paginating.

app/Http/Controllers/Admin/PermissionController.php

function __construct()
{
     $this->middleware('can:permission list', ['only' => ['index','show']]);
     $this->middleware('can:permission create', ['only' => ['create','store']]);
     $this->middleware('can:permission edit', ['only' => ['edit','update']]);
     $this->middleware('can:permission delete', ['only' => ['destroy']]);
}
public function index()
{
    $permissions = Permission::latest()->paginate(5);
    return view('admin.permission.index',compact('permissions'))->with('i', (request()->input('page', 1) - 1) * 5);
}

Create the index.blade.php view file inside the admin/permission folder.

resources/views/admin/permission/index.blade.php

<x-app-layout>
  <x-slot name="header">
    <h2 class="font-semibold text-xl text-gray-800 leading-tight">
      {{ __('Permissions') }}
    </h2>
  </x-slot>
  <div class="py-12">
    <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
      <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
        <div class="p-6 bg-white border-b border-gray-200">
          <div class="flex flex-col mt-8">
            @can('permission create')
            <div class="d-print-none with-border mb-8">
              <a href="{{ route('permission.create') }}"
                class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">{{
                __('Add Permission') }}</a>
            </div>
            @endcan
            <div class="py-2">
              @if(session()->has('message'))
              <div class="mb-8 text-green-400 font-bold">
                {{ session()->get('message') }}
              </div>
              @endif
              <div class="min-w-full border-b border-gray-200 shadow">
                <table class="border-collapse table-auto w-full text-sm">
                  <thead>
                    <tr>
                      <th
                        class="py-4 px-6 bg-grey-lightest font-bold uppercase text-sm text-grey-dark border-b border-grey-light text-left">
                        {{ __('Name') }}
                      </th>
                      @canany(['permission edit', 'permission delete'])
                      <th
                        class="py-4 px-6 bg-grey-lightest font-bold uppercase text-sm text-grey-dark border-b border-grey-light text-left">
                        {{ __('Actions') }}
                      </th>
                      @endcanany
                    </tr>
                  </thead>
                  <tbody class="bg-white dark:bg-slate-800">
                    @foreach($permissions as $permission)
                    <tr>
                      <td
                        class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">
                        <div class="text-sm text-gray-900">
                          <a href="{{route('permission.show', $permission->id)}}"
                            class="no-underline hover:underline text-cyan-600 dark:text-cyan-400">{{ $permission->name
                            }}</a>
                        </div>
                      </td>
                      @canany(['permission edit', 'permission delete'])
                      <td
                        class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">
                        <form action="{{ route('permission.destroy', $permission->id) }}" method="POST">
                          <a href="{{route('permission.edit', $permission->id)}}"
                            class="px-4 py-2 text-white mr-4 bg-blue-600">
                            {{ __('Edit') }}
                          </a>
                          @csrf
                          @method('DELETE')
                          <button class="px-4 py-2 text-white bg-red-600">
                            {{ __('Delete') }}
                          </button>
                        </form>
                      </td>
                      @endcanany
                    </tr>
                    @endforeach
                  </tbody>
                </table>
              </div>
              <div class="py-8">
                {{ $permissions->links() }}
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</x-app-layout>

This page has create, read, and update links. We have also added a delete button with the form. Update the destroy function with the below code: -

app/Http/Controllers/Admin/PermissionController.php

public function destroy(Permission $permission)
{
    $permission->delete();
    return redirect()->route('permission.index')->with('message','Permission deleted successfully');
}

Step 6: Create operation

Update the create function with the below code and create create.blade.php view file.

app/Http/Controllers/Admin/PermissionController.php

public function create()
{
    return view('admin.permission.create');
}

resources/views/admin/permission/create.blade.php

<x-app-layout>
  <x-slot name="header">
    <h2 class="font-semibold text-xl text-gray-800 leading-tight">
      {{ __('Permissions') }}
    </h2>
  </x-slot>
  <div class="py-12">
    <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
      <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
        <div class="px-6 flex justify-between items-center">
          <h1
            class="inline-block text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">
            {{ __('Create permission') }}</h1>
          <a href="{{route('permission.index')}}" class="px-4 py-2 text-white mr-4 bg-blue-600">{{ __('Back to all
            permission') }}</a>
          @if ($errors->any())
          <ul class="mt-3 list-none list-inside text-sm text-red-400">
            @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
            @endforeach
          </ul>
          @endif
        </div>
        <div class="w-full px-6 py-4 bg-white overflow-hidden">
          <form method="POST" action="{{ route('permission.store') }}">
            @csrf
            <div class="py-2">
              <label for="name"
                class="block font-medium text-sm text-gray-700{{$errors->has('name') ? ' text-red-400' : ''}}">{{
                __('Name') }}</label>
              <input id="name"
                class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('name') ? ' border-red-400' : ''}}"
                type="text" name="name" value="{{ old('name') }}" />
            </div>
            <div class="flex justify-end mt-4">
              <button type='submit'
                class='inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out duration-150'>
                {{ __('Create') }}
              </button>
            </div>
          </form>
        </div>
      </div>
    </div>
  </div>
</x-app-layout>

Navigate the http://127.0.0.1:8000/admin/permission/create URL in the browser.

The submit action will call the store function. So copy the below code to the store function

app/Http/Controllers/Admin/PermissionController.php

public function store(Request $request)
{
    $request->validate(['name' => 'required|string|max:255|unique:'.config('permission.table_names.permissions', 'permissions').',name',]);
    Permission::create(['name' => $request->name , 'guard_name'=> 'web' ]);
    return redirect()->route('permission.index')->with('message','Permission created successfully.');
}

The $request->validate is used to validate the create form.

Step 7: Update operation

We will use two functions used for the update. The edit function for form display and update function to save the form.

app/Http/Controllers/Admin/PermissionController.php

public function edit(Permission $permission)
{
    return view('admin.permission.edit',compact('permission'));
}

public function update(Request $request, Permission $permission)
{
  $request->validate(['name' => 'required|string|max:255|unique:'.config('permission.table_names.permissions', 'permissions').',name,'.$permission->id,]);
  $permission->update(['name' => $request->name , 'guard_name'=> 'web' ]);
  return redirect()->route('permission.index')->with('message','Permission updated successfully.');
}

resources/views/admin/permission/edit.blade.php

Step 8: View operation

This is the final step for our permission CRUD. The show function is used for the view operation.

app/Http/Controllers/Admin/PermissionController.php

public function show(Permission $permission)
{
    return view('admin.permission.show',compact('permission'));
}

resources/views/admin/permission/show.blade.php

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Permissions') }}
        </h2>
    </x-slot>
		<div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="px-6 flex justify-between items-center">
                    <h1 class="inline-block text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">{{ __('View permission') }}</h1>
                    <a href="{{route('permission.index')}}" class="px-4 py-2 text-white mr-4 bg-blue-600">{{ __('Back to all permission') }}</a>
                    @if ($errors->any())
                        <ul class="mt-3 list-none list-inside text-sm text-red-400">
                            @foreach ($errors->all() as $error)
                                <li>{{ $error }}</li>
                            @endforeach
                        </ul>
                    @endif
                </div>
                <div class="w-full px-6 py-4">
                    <div class="min-w-full border-b border-gray-200 shadow">
                        <table class="table-fixed w-full text-sm">
                            <tbody class="bg-white dark:bg-slate-800">
                                <tr>
                                    <td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ __('Name') }}</td>
                                    <td class="border-b border-slate-100 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400">{{$permission->name}}</td>
                                </tr>
                                <tr>
                                    <td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ __('Created') }}</td>
                                    <td class="border-b border-slate-100 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400">{{$permission->created_at}}</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

We have successfully created our first Laravel CRUD. This permission CRUD is open for all authenticated users. So for the next part, we need to add permission-based access restriction to our permission CRUD.

For the Roles and User

Step 1: Create Model

php artisan make:model Role

app/Models/Role.php

<?php
namespace App\\Models;
use Spatie\\Permission\\Models\\Role as OriginalRole;

class Role extends OriginalRole
{
    protected $fillable = [
        'name',
        'guard_name',
        'updated_at',
        'created_at'
    ];
}

Step 2: Create a controller

php artisan make:controller Admin/RoleController --model=Role --resource
php artisan make:controller Admin/UserController --model=User --resource

Step 3: Add Routes

We have added two routes: -

  1. role

  2. user

Route::prefix('admin')->namespace('App\\Http\\Controllers\\Admin')->middleware(['auth'])->group(function() {
    Route::resource('permission', PermissionController::class);
    Route::resource('role', RoleController::class);
    Route::resource('user', UserController::class);
});

Step 4: Add a link to the navigation

<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
	<x-nav-link :href="route('dashboard')" 
		:active="request()->routeIs('dashboard')">
		{{ __('Dashboard') }}
	</x-nav-link>
	<x-nav-link :href="route('permission.index')" 
		:active="request()->routeIs('permission.index')">
		{{ __('Permission') }}
	</x-nav-link>
	<x-nav-link :href="route('role.index')" 
		:active="request()->routeIs('role.index')">{{ __('Roles') }}
	</x-nav-link>
	<x-nav-link :href="route('user.index')" 
		:active="request()->routeIs('user.index')">{{ __('Users') }}
	</x-nav-link>
</div>

For mobile navigation

<!-- Responsive Navigation Menu -->
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
  <div class="pt-2 pb-3 space-y-1">
    <x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
      {{ __('Dashboard') }}
    </x-responsive-nav-link>
    <x-responsive-nav-link :href="route('permission.index')" :active="request()->routeIs('permission.index')">
      {{ __('Permission') }}
    </x-responsive-nav-link>
    <x-responsive-nav-link :href="route('role.index')" :active="request()->routeIs('role.index')">
      {{ __('Roles') }}
    </x-responsive-nav-link>
    <x-responsive-nav-link :href="route('user.index')" :active="request()->routeIs('user.index')">
      {{ __('Users') }}
    </x-responsive-nav-link>
  </div>

Step 5: Index page with action links

  1. Roles

Update index function with permission paginating.

app/Http/Controllers/Admin/RoleController.php

function __construct()
{
     $this->middleware('can:role list', ['only' => ['index','show']]);
     $this->middleware('can:role create', ['only' => ['create','store']]);
     $this->middleware('can:role edit', ['only' => ['edit','update']]);
     $this->middleware('can:role delete', ['only' => ['destroy']]);
}
public function index()
{
    $roles = (new Role)->newQuery();
    if (request()->has('search')) {
        $roles->where('name', 'Like', '%' . request()->input('search') . '%');
    }
    if (request()->query('sort')) {
        $attribute = request()->query('sort');
        $sort_order = 'ASC';
        if (strncmp($attribute, '-', 1) === 0) {
            $sort_order = 'DESC';
            $attribute = substr($attribute, 1);
        }
        $roles->orderBy($attribute, $sort_order);
    } else {
        $roles->latest();
    }
    $roles = $roles->paginate(5);
    return view('admin.role.index',compact('roles'))
        ->with('i', (request()->input('page', 1) - 1) * 5);
}

Create the index.blade.php view file inside the admin/role folder.

resources/views/admin/role/index.blade.php

<x-app-layout>
	<x-slot name="header">
		<h2 class="font-semibold text-xl text-gray-800 leading-tight">
			{{ __('Roles') }}
		</h2>
	</x-slot>
	<div class="py-12">
		<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
			<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
				<div class="p-6 bg-white border-b border-gray-200">
					<div class="flex flex-col mt-8">
						@can('role create')
						<div class="d-print-none with-border mb-8">
							<a href="{{ route('role.create') }}"
								class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">{{
								__('Add Role') }}</a>
						</div>
						@endcan
						<div class="py-2">
							@if(session()->has('message'))
							<div class="mb-8 text-green-400 font-bold">
								{{ session()->get('message') }}
							</div>
							@endif
							<div class="min-w-full border-b border-gray-200 shadow">
								<form method="GET" action="{{ route('role.index') }}">
									<div class="py-2 flex">
										<div class="overflow-hidden flex pl-4">
											<input type="search" name="search" value="{{ request()->input('search') }}"
												class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
												placeholder="Search">
											<button type='submit'
												class='ml-4 inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out duration-150'>
												{{ __('Search') }}
											</button>
										</div>
									</div>
								</form>
								<table class="border-collapse table-auto w-full text-sm">
									<thead>
										<tr>
											<th
												class="py-4 px-6 bg-grey-lightest font-bold uppercase text-sm text-grey-dark border-b border-grey-light text-left">

											</th>
											@canany(['role edit', 'role delete'])
											<th
												class="py-4 px-6 bg-grey-lightest font-bold uppercase text-sm text-grey-dark border-b border-grey-light text-left">
												{{ __('Actions') }}
											</th>
											@endcanany
										</tr>
									</thead>
									<tbody class="bg-white dark:bg-slate-800">
										@foreach($roles as $role)
										<tr>
											<td
												class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">
												<div class="text-sm text-gray-900">
													<a href="{{route('role.show', $role->id)}}"
														class="no-underline hover:underline text-cyan-600 dark:text-cyan-400">{{ $role->name }}</a>
												</div>
											</td>
											@canany(['role edit', 'role delete'])
											<td
												class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">
												<form action="{{ route('role.destroy', $role->id) }}" method="POST">
													@can('role edit')
													<a href="{{route('role.edit', $role->id)}}" class="px-4 py-2 text-white mr-4 bg-blue-600">
														{{ __('Edit') }}
													</a>
													@endcan
													@can('role delete')
													@csrf
													@method('DELETE')
													<button class="px-4 py-2 text-white bg-red-600">
														{{ __('Delete') }}
													</button>
													@endcan
												</form>
											</td>
											@endcanany
										</tr>
										@endforeach
									</tbody>
								</table>
							</div>
							<div class="py-8">
								{{ $roles->appends(request()->query())->links() }}
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>
</x-app-layout>

Don’t forget the run the npm run dev to rebuild the CSS.

This page has create, read, and update links. We have also added a delete button with the form. Update the destroy function with the below code.

app/Http/Controllers/Admin/RoleController.php

public function destroy(Role $role)
{
    $role->delete();
    return redirect()->route('role.index')
                    ->with('message','Role deleted successfully');
}

2 . User

Create the index.blade.php, create.blade.php, edit.blade.php, and show.blade.php files inside the admin/user folder.

resources/views/admin/user/index.blade.php

<x-app-layout>
	<x-slot name="header">
		<h2 class="font-semibold text-xl text-gray-800 leading-tight">
			{{ __('Users') }}
		</h2>
	</x-slot>
	<div class="py-12">
		<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
			<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
				<div class="p-6 bg-white border-b border-gray-200">
					<div class="flex flex-col mt-8">
						@can('user create')
						<div class="d-print-none with-border mb-8">
							<a href="{{ route('user.create') }}"
								class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">{{
								__('Add User') }}</a>
						</div>
						@endcan
						<div class="py-2">
							@if(session()->has('message'))
							<div class="mb-8 text-green-400 font-bold">
								{{ session()->get('message') }}
							</div>
							@endif
							<div class="min-w-full border-b border-gray-200 shadow">
								<form method="GET" action="{{ route('user.index') }}">
									<div class="py-2 flex">
										<div class="overflow-hidden flex pl-4">
											<input type="search" name="search" value="{{ request()->input('search') }}"
												class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
												placeholder="Search">
											<button type='submit'
												class='ml-4 inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out duration-150'>
												{{ __('Search') }}
											</button>
										</div>
									</div>
								</form>
								<table class="border-collapse table-auto w-full text-sm">
									<thead>
										<tr>
											<th
												class="py-4 px-6 bg-grey-lightest font-bold uppercase text-sm text-grey-dark border-b border-grey-light text-left">
												{{ __('Name')}}
											</th>
											<th
												class="py-4 px-6 bg-grey-lightest font-bold uppercase text-sm text-grey-dark border-b border-grey-light text-left">
												{{ __('Email')}}
											</th>
											@canany(['user edit', 'user delete'])
											<th
												class="py-4 px-6 bg-grey-lightest font-bold uppercase text-sm text-grey-dark border-b border-grey-light text-left">
												{{ __('Actions') }}
											</th>
											@endcanany
										</tr>
									</thead>
									<tbody class="bg-white dark:bg-slate-800">
										@foreach($users as $user)
										<tr>
											<td
												class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">
												<div class="text-sm text-gray-900">
													<a href="{{route('user.show', $user->id)}}"
														class="no-underline hover:underline text-cyan-600 dark:text-cyan-400">{{ $user->name }}</a>
												</div>
											</td>
											<td
												class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">
												<div class="text-sm text-gray-900">
													{{ $user->email }}
												</div>
											</td>
											@canany(['user edit', 'user delete'])
											<td
												class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">
												<form action="{{ route('user.destroy', $user->id) }}" method="POST">
													@can('user edit')
													<a href="{{route('user.edit', $user->id)}}" class="px-4 py-2 text-white mr-4 bg-blue-600">
														{{ __('Edit') }}
													</a>
													@endcan
													@can('user delete')
													@csrf
													@method('DELETE')
													<button class="px-4 py-2 text-white bg-red-600">
														{{ __('Delete') }}
													</button>
													@endcan
												</form>
											</td>
											@endcanany
										</tr>
										@endforeach
									</tbody>
								</table>
							</div>
							<div class="py-8">
								{{ $users->appends(request()->query())->links() }}
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>
</x-app-layout>

app/Http/Controllers/Admin/UserController.php

function __construct()
{
     $this->middleware('can:user list', ['only' => ['index','show']]);
     $this->middleware('can:user create', ['only' => ['create','store']]);
     $this->middleware('can:user edit', ['only' => ['edit','update']]);
     $this->middleware('can:user delete', ['only' => ['destroy']]);
}
/**
 * Display a listing of the resource.
 *
 * @return \Illuminate\Http\Response
 */
public function index()
{
    $users = (new User)->newQuery();
    if (request()->has('search')) {
        $users->where('name', 'Like', '%' . request()->input('search') . '%');
    }
    if (request()->query('sort')) {
        $attribute = request()->query('sort');
        $sort_order = 'ASC';
        if (strncmp($attribute, '-', 1) === 0) {
            $sort_order = 'DESC';
            $attribute = substr($attribute, 1);
        }
        $users->orderBy($attribute, $sort_order);
    } else {
        $users->latest();
    }
    $users = $users->paginate(5);
    return view('admin.user.index',compact('users'))
        ->with('i', (request()->input('page', 1) - 1) * 5);
}
public function destroy(User $user)
{
    $user->delete();
    return redirect()->route('user.index')
                    ->with('message','User deleted successfully');
}

Step 6: Create operation

  1. Role

Update the create function with the below code and create create.blade.php view file. The Permission::all() is used to list the permission on create page.

app/Http/Controllers/Admin/RoleController.php

public function create()
{
    $permissions = Permission::all();
    return view('admin.role.create', compact('permissions'));
}

resources/views/admin/role/create.blade.php

<x-app-layout>
	<x-slot name="header">
		<h2 class="font-semibold text-xl text-gray-800 leading-tight">
			{{ __('Roles') }}
		</h2>
	</x-slot>
	<div class="py-12">
		<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
			<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
				<div class="px-6 flex justify-between items-center">
					<h1
						class="inline-block text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">
						{{ __('Create role') }}</h1>
					<a href="{{route('role.index')}}" class="px-4 py-2 text-white mr-4 bg-blue-600">{{ __('Back to all role')
						}}</a>
					@if ($errors->any())
					<ul class="mt-3 list-none list-inside text-sm text-red-400">
						@foreach ($errors->all() as $error)
						<li>{{ $error }}</li>
						@endforeach
					</ul>
					@endif
				</div>
				<div class="w-full px-6 py-4 bg-white overflow-hidden">
					<form method="POST" action="{{ route('role.store') }}">
						@csrf
						<div class="py-2">
							<label for="name"
								class="block font-medium text-sm text-gray-700{{$errors->has('name') ? ' text-red-400' : ''}}">{{
								__('Name') }}</label>
							<input id="name"
								class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('name') ? ' border-red-400' : ''}}"
								type="text" name="name" value="{{ old('name') }}" />
						</div>
						<div class="py-2">
							<h3
								class="inline-block text-xl sm:text-2xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">
								Permissions</h3>
							<div class="grid grid-cols-4 gap-4">
								@forelse ($permissions as $permission)
								<div class="col-span-4 sm:col-span-2 md:col-span-1">
									<label class="form-check-label">
										<input type="checkbox" name="permissions[]" value="{{ $permission->name }}"
											class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
										{{ $permission->name }}
									</label>
								</div>
								@empty
								----
								@endforelse
							</div>
						</div>
						<div class="flex justify-end mt-4">
							<button type='submit'
								class='inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out duration-150'>
								{{ __('Create') }}
							</button>
						</div>
					</form>
				</div>
			</div>
		</div>
	</div>
</x-app-layout>

Navigate the http://127.0.0.1:8000/admin/role/create URL in the browser.

The submit action will call the store function. So copy the below code in the store function.

app/Http/Controllers/Admin/RoleController.php

public function store(Request $request)
{
    $request->validate([
        'name' => 'required|string|max:255|unique:'.config('permission.table_names.roles', 'roles').',name',
    ]);
    $role = Role::create($request->all());
    if(! empty($request->permissions)) {
        $role->givePermissionTo($request->permissions);
    }
    return redirect()->route('role.index')
                    ->with('message','Role created successfully.');
}

2 . User

Users, in this case, are anyone who asks for permission to your database. They are usually the people in your organization.

app/Http/Controllers/Admin/UserController.php

Extend model in user controller

use App\Models\Role;
use App\Models\Permission;
use Spatie\Permission\Models\Role as OriginalRole;
use Spatie\Permission\Models\Permission as OriginalPermission;
public function create()
{
    $roles = Role::all();
    return view('admin.user.create', compact('roles'));
}
public function store(Request $request)
{
	$request->validate([
		'name' => ['required', 'string', 'max:255'],
		'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
		'password' => ['required', 'confirmed', Rules\Password::defaults()],
	]);
	$user = User::create([
		'name' => $request->name,
		'email' => $request->email,
		'password' => Hash::make($request->password),
	]);
	if(! empty($request->roles)) {
		$user->assignRole($request->roles);
	}
	return redirect()->route('user.index')->with('message','User created successfully.');
}

resources/views/admin/user/create.blade.php

<x-app-layout>
	<x-slot name="header">
		<h2 class="font-semibold text-xl text-gray-800 leading-tight">
			{{ __('Users') }}
		</h2>
	</x-slot>
	<div class="py-12">
		<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
			<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
				<div class="px-6 flex justify-between items-center">
					<h1 class="inline-block text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">{{ __('Create user') }}</h1>
					<a href="{{route('user.index')}}" class="px-4 py-2 text-white mr-4 bg-blue-600">{{ __('Back to all users') }}</a>
					@if ($errors->any())
					<ul class="mt-3 list-none list-inside text-sm text-red-400">
						@foreach ($errors->all() as $error)
						<li>{{ $error }}</li>
						@endforeach
					</ul>
					@endif
				</div>
				<div class="w-full px-6 py-4 bg-white overflow-hidden">
					<form method="POST" action="{{ route('user.store') }}">
						@csrf
						<div class="py-2">
							<label for="name" class="block font-medium text-sm text-gray-700{{$errors->has('name') ? ' text-red-400' : ''}}">{{ __('Name') }}</label>
							<input id="name" class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('name') ? ' border-red-400' : ''}}" type="text" name="name" value="{{ old('name') }}" />
						</div>
						<div class="py-2">
							<label for="email" class="block font-medium text-sm text-gray-700{{$errors->has('email') ? ' text-red-400' : ''}}">{{ __('Email') }}</label>
							<input id="email" class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('email') ? ' border-red-400' : ''}}" type="email" name="email" value="{{ old('email') }}" />
						</div>
						<div class="py-2">
							<label for="password" class="block font-medium text-sm text-gray-700{{$errors->has('password') ? ' text-red-400' : ''}}">{{ __('Password') }}</label>
							<input id="password" class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('password') ? ' border-red-400' : ''}}" type="password" name="password" />
						</div>
						<div class="py-2">
							<label for="password_confirmation" class="block font-medium text-sm text-gray-700{{$errors->has('password') ? ' text-red-400' : ''}}">{{ __('Password Confirmation') }}</label>
							<input id="password_confirmation" class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('password') ? ' border-red-400' : ''}}" type="password" name="password_confirmation" />
						</div>
						<div class="py-2">
							<h3 class="inline-block text-xl sm:text-2xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">Roles</h3>
							<div class="grid grid-cols-4 gap-4">
								@forelse ($roles as $role)
								<div class="col-span-4 sm:col-span-2 md:col-span-1">
									<label class="form-check-label">
										<input type="checkbox" name="roles[]" value="{{ $role->name }}" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
										{{ $role->name }}
									</label>
								</div>
								@empty
								----
								@endforelse
							</div>
						</div>
						<div class="flex justify-end mt-4">
							<button type='submit' class='inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out duration-150'>
								{{ __('Create') }}
							</button>
						</div>
					</form>
				</div>
			</div>
		</div>
	</div>
</x-app-layout>

Step 7: Update operation

  1. Role

We will use two functions used for the update. The edit function for form display and update function to save the form.

app/Http/Controllers/Admin/RoleController.php

Export spatie role and permission class

use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
public function edit(Role $role)
{
    $permissions = Permission::all();
    $roleHasPermissions = array_column(json_decode($role->permissions, true), 'id');
    return view('admin.role.edit',compact('role', 'permissions', 'roleHasPermissions'));
}

public function update(Request $request, Role $role)
{
    $request->validate([
        'name' => 'required|string|max:255|unique:'.config('permission.table_names.roles', 'roles').',name,'.$role->id,
    ]);
    
    $role->update(['name' => $request->name , 'guard_name'=> 'web' ]);
    $permissions = $request->permissions ?? [];
    $role->syncPermissions($permissions);
    return redirect()->route('role.index')
                    ->with('message','Role updated successfully.');
}

resources/views/admin/role/edit.blade.php

<x-app-layout>
	<x-slot name="header">
		<h2 class="font-semibold text-xl text-gray-800 leading-tight">
			{{ __('Roles') }}
		</h2>
	</x-slot>
	<div class="py-12">
		<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
			<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
				<div class="px-6 flex justify-between items-center">
					<h1 class="inline-block text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">{{ __('Update role') }}</h1>
					<a href="{{route('role.index')}}" class="px-4 py-2 text-white mr-4 bg-blue-600">{{ __('Back to all role') }}</a>
					@if ($errors->any())
					<ul class="mt-3 list-none list-inside text-sm text-red-400">
						@foreach ($errors->all() as $error)
						<li>{{ $error }}</li>
						@endforeach
					</ul>
					@endif
				</div>
				<div class="w-full px-6 py-4 bg-white overflow-hidden">
					<form method="POST" action="{{ route('role.update', $role->id) }}">
						@csrf
						@method('PUT')
						<div class="py-2">
							<label for="name" class="block font-medium text-sm text-gray-700{{$errors->has('name') ? ' text-red-400' : ''}}">{{ __('Name') }}</label>
							<input id="name" class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('name') ? ' border-red-400' : ''}}" type="text" name="name" value="{{ old('name', $role->name) }}" />
						</div>
						@unless ($role->name == env('APP_SUPER_ADMIN', 'super-admin'))
						<div class="py-2">
							<h3 class="inline-block text-xl sm:text-2xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">Permissions</h3>
							<div class="grid grid-cols-4 gap-4">
								@forelse ($permissions as $permission)
								<div class="col-span-4 sm:col-span-2 md:col-span-1">
									<label class="form-check-label">
										<input type="checkbox" name="permissions[]" value="{{ $permission->name }}" {{ in_array($permission->id, $roleHasPermissions) ? 'checked' : '' }} class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
										{{ $permission->name }}
									</label>
								</div>
								@empty
								----
								@endforelse
							</div>
						</div>
						@endunless
						<div class="flex justify-end mt-4">
							<button type='submit' class='inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out duration-150'>
								{{ __('Update') }}
							</button>
						</div>
					</form>
				</div>
			</div>
		</div>
	</div>
</x-app-layout>
  1. User

You need to make sure that the users of your database get the right set of permissions so that the security of your data remains strong.

resources/views/admin/user/edit.blade.php

<x-app-layout>
	<x-slot name="header">
		<h2 class="font-semibold text-xl text-gray-800 leading-tight">
			{{ __('Users') }}
		</h2>
	</x-slot>
	<div class="py-12">
		<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
			<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
				<div class="px-6 flex justify-between items-center">
					<h1 class="inline-block text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">{{ __('Update user') }}</h1>
					<a href="{{route('user.index')}}" class="px-4 py-2 text-white mr-4 bg-blue-600">{{ __('Back to all users') }}</a>
					@if ($errors->any())
					<ul class="mt-3 list-none list-inside text-sm text-red-400">
						@foreach ($errors->all() as $error)
						<li>{{ $error }}</li>
						@endforeach
					</ul>
					@endif
				</div>
				<div class="w-full px-6 py-4 bg-white overflow-hidden">
					<form method="POST" action="{{ route('user.update', $user->id) }}">
						@csrf
						@method('PUT')
						<div class="py-2">
							<label for="name" class="block font-medium text-sm text-gray-700{{$errors->has('name') ? ' text-red-400' : ''}}">{{ __('Name') }}</label>
							<input id="name" class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('name') ? ' border-red-400' : ''}}" type="text" name="name" value="{{ old('name', $user->name) }}" />
						</div>
						<div class="py-2">
							<label for="email" class="block font-medium text-sm text-gray-700{{$errors->has('email') ? ' text-red-400' : ''}}">{{ __('Email') }}</label>
							<input id="email" class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('email') ? ' border-red-400' : ''}}" type="email" name="email" value="{{ old('email', $user->email) }}" />
						</div>
						<div class="py-2">
							<label for="password" class="block font-medium text-sm text-gray-700{{$errors->has('password') ? ' text-red-400' : ''}}">{{ __('Password') }}</label>
							<input id="password" class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('password') ? ' border-red-400' : ''}}" type="password" name="password" />
						</div>
						<div class="py-2">
							<label for="password_confirmation" class="block font-medium text-sm text-gray-700{{$errors->has('password') ? ' text-red-400' : ''}}">{{ __('Password Confirmation') }}</label>
							<input id="password_confirmation" class="rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 block mt-1 w-full{{$errors->has('password') ? ' border-red-400' : ''}}" type="password" name="password_confirmation" />
						</div>
						<div class="py-2">
							<h3 class="inline-block text-xl sm:text-2xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">Roles</h3>
							<div class="grid grid-cols-4 gap-4">
								@forelse ($roles as $role)
								<div class="col-span-4 sm:col-span-2 md:col-span-1">
									<label class="form-check-label">
										<input type="checkbox" name="roles[]" value="{{ $role->name }}" {{ in_array($role->id, $userHasRoles) ? 'checked' : '' }} class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
										{{ $role->name }}
									</label>
								</div>
								@empty
								----
								@endforelse
							</div>
						</div>
						<div class="flex justify-end mt-4">
							<button type='submit' class='inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 active:bg-gray-900 focus:outline-none focus:border-gray-900 focus:ring ring-gray-300 disabled:opacity-25 transition ease-in-out duration-150'>
								{{ __('Update') }}
							</button>
						</div>
					</form>
				</div>
			</div>
		</div>
	</div>
</x-app-layout>

app/Http/Controllers/Admin/UserController.php

public function edit(User $user)
{
	$roles = Role::all();
	$userHasRoles = array_column(json_decode($user->roles, true), 'id');
	return view('admin.user.edit', compact('user', 'roles', 'userHasRoles'));
}
public function update(Request $request, User $user)
{
	$request->validate([
		'name' => ['required', 'string', 'max:255'],
		'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,'.$user->id],
		'password' => ['nullable','confirmed', Rules\\Password::defaults()],
	]);
	$user->update([
		'name' => $request->name,
		'email' => $request->email,
	]);
	if($request->password){
		$user->update([
			'password' => Hash::make($request->password),
		]);
	}
	$roles = $request->roles ?? [];
	$user->syncRoles($roles);
	return redirect()->route('user.index')->with('message','User updated successfully.');
}

8. View operation

  1. Role

Here, with this operation, we will be viewing the roles of the users to whom you have granted the permissions.

app/Http/Controllers/Admin/RoleController.php

public function show(Role $role)
{
    $permissions = Permission::all();
    $roleHasPermissions = array_column(json_decode($role->permissions, true), 'id');
    return view('admin.role.show', compact('role', 'permissions', 'roleHasPermissions'));
}

resources/views/admin/role/show.blade.php

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Roles') }}
        </h2>
    </x-slot>
<div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="px-6 flex justify-between items-center">
                    <h1 class="inline-block text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">{{ __('View role') }}</h1>
                    <a href="{{route('role.index')}}" class="px-4 py-2 text-white mr-4 bg-blue-600">{{ __('Back to all role') }}</a>
                    @if ($errors->any())
                        <ul class="mt-3 list-none list-inside text-sm text-red-400">
                            @foreach ($errors->all() as $error)
                                <li>{{ $error }}</li>
                            @endforeach
                        </ul>
                    @endif
                </div>
                <div class="w-full px-6 py-4">
                    <div class="min-w-full border-b border-gray-200 shadow">
                        <table class="table-fixed w-full text-sm">
                            <tbody class="bg-white dark:bg-slate-800">
                                <tr>
                                    <td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ __('Name') }}</td>
                                    <td class="border-b border-slate-100 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400">{{$role->name}}</td>
                                </tr>
                                <tr>
                                @unless ($role->name == env('APP_SUPER_ADMIN', 'super-admin'))
                                <td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ __('Permissions') }}</td>
                                    <td class="border-b border-slate-100 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400">
<div class="py-2">
                                        <div class="grid grid-cols-4 gap-4">
                                            @forelse ($permissions as $permission)
                                                <div class="col-span-4 sm:col-span-2 md:col-span-2">
                                                    <label class="form-check-label">
                                                        <input type="checkbox" name="permissions[]" value="{{ $permission->name }}" {{ in_array($permission->id, $roleHasPermissions) ? 'checked' : '' }} disabled="disabled" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
                                                        {{ $permission->name }}
                                                    </label>
                                                </div>
                                            @empty
                                                ----
                                            @endforelse
                                        </div>
                                    </div>
                                    </td>
                                </tr>
                                @endunless
                                <tr>
                                    <td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ __('Created') }}</td>
                                    <td class="border-b border-slate-100 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400">{{$role->created_at}}</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>
  1. User

Here is how you can view users of your database along with their other details such as their assigned permissions.

app/Http/Controllers/Admin/UserController.php

public function show(User $user)
{
	$roles = Role::all();
	$userHasRoles = array_column(json_decode($user->roles, true), 'id');
	return view('admin.user.show', compact('user', 'roles', 'userHasRoles'));
}

resources/views/admin/user/show.blade.php

<x-app-layout>
	<x-slot name="header">
		<h2 class="font-semibold text-xl text-gray-800 leading-tight">
			{{ __('Users') }}
		</h2>
	</x-slot>
	<div class="py-12">
		<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
			<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
				<div class="px-6 flex justify-between items-center">
					<h1 class="inline-block text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200 py-4 block sm:inline-block flex">{{ __('View user') }}</h1>
					<a href="{{route('user.index')}}" class="px-4 py-2 text-white mr-4 bg-blue-600">{{ __('Back to all users') }}</a>
					@if ($errors->any())
					<ul class="mt-3 list-none list-inside text-sm text-red-400">
						@foreach ($errors->all() as $error)
						<li>{{ $error }}</li>
						@endforeach
					</ul>
					@endif
				</div>
				<div class="w-full px-6 py-4">
					<div class="min-w-full border-b border-gray-200 shadow">
						<table class="table-fixed w-full text-sm">
							<tbody class="bg-white dark:bg-slate-800">
								<tr>
									<td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ __('Name') }}</td>
									<td class="border-b border-slate-100 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400">{{$user->name}}</td>
								</tr>
								<tr>
									<td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ __('Email') }}</td>
									<td class="border-b border-slate-100 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400">{{$user->email}}</td>
								</tr>
								<tr>
									<td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ __('Roles') }}</td>
									<td class="border-b border-slate-100 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400">
										<div class="py-2">
											<div class="grid grid-cols-4 gap-4">
												@forelse ($roles as $role)
												<div class="col-span-4 sm:col-span-2 md:col-span-2">
													<label class="form-check-label">
														<input type="checkbox" name="roles[]" value="{{ $role->name }}" {{ in_array($role->id, $userHasRoles) ? 'checked' : '' }} disabled="disabled" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
														{{ $role->name }}
													</label>
												</div>
												@empty
												----
												@endforelse
											</div>
										</div>
									</td>
								</tr>
								<tr>
									<td class="border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400">{{ __('Created') }}</td>
									<td class="border-b border-slate-100 dark:border-slate-700 p-4 text-slate-500 dark:text-slate-400">{{$user->created_at}}</td>
								</tr>
							</tbody>
						</table>
					</div>
				</div>
			</div>
		</div>
	</div>
</x-app-layout>

Add link in the dropdown

<x-slot name="content">
	<!-- Authentication -->
	<x-dropdown-link :href="route('admin.profile.info')" :active="request()->routeIs('admin.account.info')">
	{{ __('My Account') }}
		</x-dropdown-link>

Add function in user controller

app/Http/Controllers/Admin/UserController.php

public function profileInformation() {
    $user = Auth::user();
    return view('admin.user.profile',compact('user'));
}

public function profileInformationStore(Request $request)
{
    $request->validateWithBag('account', [
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email,'.\Auth::user()->id],
    ]);
    $user = \Auth::user()->update($request->except(['_token']));
    if ($user) {
        $message = "Account updated successfully.";
    } else {
        $message = "Error while saving. Please try again.";
    }
    return redirect()->route('admin.profile.info')->with('account_message', $message);
}
public function changePasswordStore(Request $request)
{
    $validator = \Validator::make($request->all(), [
        'old_password' => ['required'],
        'new_password' => ['required', Rules\Password::defaults()],
        'confirm_password' => ['required', 'same:new_password', Rules\Password::defaults()],
    ]);
    $validator->after(function ($validator) use ($request) {
        if ($validator->failed()) return;
        if (! Hash::check($request->input('old_password'), \Auth::user()->password)) {
            $validator->errors()->add(
                'old_password', 'Old password is incorrect.'
            );
        }
    });
    $validator->validateWithBag('password');
    $user = \Auth::user()->update([
        'password' => Hash::make($request->input('new_password')),
    ]);
    if ($user) {
        $message = "Password updated successfully.";
    } else {
        $message = "Error while saving. Please try again.";
    }
    return redirect()->route('admin.profile.info')->with('password_message', $message);
} 

If you are wondering about the source of this code and want to have a look, then here is the link.

Github Link - https://github.com/acquaint-softtech/laravel-permission-demo

If you are wondering about the source of this code and want to have a look, then here is the link.

https://permission-demo.acquaintsoft.com/

Here is the demo link. Have a look to see it all in even more detail.

Conclusion

Assigning permissions to users is necessary for sake of the security of your data. If you have followed along with the above-mentioned steps, I know that you also would have been able to create permissions and assign roles easily.

Mukesh Ram

Founder and CEO, Acquaint Softtech

I love to make a difference. Thus, I started Acquaint Softtech with the vision of making developers easily accessible and affordable to all. Me and my beloved team have been fulfilling this vision for over 15 years now and will continue to get even bigger and better.

Share this on

Subscribe to new posts

Other Interesting Readings