Contents
Intro
Hi everyone Today we will show to you an example of User Roles and Permissions using Laravel 8.
In this example we will implement Laravel 8 user roles and permissions tutorial using spatie/laravel-permission.
In this tutorial we will use Spatie GitHub package for roles and permissions in Laravel 8. In this post also we will explain step by step Laravel 8 ACL tutorial.
Spatie roles and permissions composer package provide way to create ACL in Laravel 8. They provide how to assign role to user, how to assign permission to user and how to assign permission assign to roles.
Step 1 – Create Laravel 8 Project
composer create-project --prefer-dist laravel/laravel mini-crm-app
Step 2 – Install Package spatie/laravel-permission
composer require spatie/laravel-permission
Step 3 – Install Package laravelcollective/html
composer require laravelcollective/html
Step 4 – Add Spatie on Provider Classes List
Now open config/app.php file and add service provider and alias.
'providers' => [ .... Spatie\Permission\PermissionServiceProvider::class, ],
Now we need to publish this vendor or package using Artisan commands on terminal as shown below.
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
After that we can see the file permission.php and another one generated on database/migrations , so for that we have to run the migration command to extract it to the database.
php artisan migrate
Step 5 – Create Product Migration
php artisan make:migration create_products_table
After creation of this file, we have to go to the directory database/migrations and find the file that refer to the create_products_table and add some extra line of codes to define all the types of data that need to enter to this table.
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateProductsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('name'); $table->text('detail'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('products'); } }
Now we have to run the migration like below:
php artisan migrate
Step 6 – Create User and Product Models
To create the models via PHP Artisan run the command as below:
php artisan make:model User
php artisan make:model Product
Make sure that both files of models to look like example below:
<?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Spatie\Permission\Traits\HasRoles; class User extends Authenticatable { use HasFactory, Notifiable, HasRoles; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast to native types. * * @var array */ protected $casts = [ 'email_verified_at' => 'datetime', ]; }
app/Models/Product.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Product extends Model { use HasFactory; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'detail' ]; }
Step 7 – Add Middleware
After the Spatie Package installation, this package also provide us middleware that we can use it to display Role and Permission. All we have to do is to go to app/Http/Kernel.php and add some codes rows.
.... protected $routeMiddleware = [ .... 'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class, 'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class, 'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class, ] ....
Step 7 – Create Authentication Kit
There are several Packets to use for Authentication, such as Laravel UI that its related with LaravelCollective, also there is Breeze that works at same way, using only HTML and more modern way is Jetstream that works with Vue templates, and its followed by two Sub Packets like LiveWire and Inertia.
In this tutorial we have to go with laravel/ui. So import it as below:
composer require laravel/ui
After this we need to generate the authentication scaffolding using PHP Artisan:
php artisan ui bootstrap --auth
After that we have to install all NPM extensions
npm install
And then we have to deploy UI in development mode:
npm run dev
If we want to deploy the UI in Production Mode we have to run
npm run build
and all the HTML files or CSS files or Js files will convert into minified format.
Step 8 – Create Routes
<?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\HomeController; use App\Http\Controllers\RoleController; use App\Http\Controllers\UserController; use App\Http\Controllers\ProductController; Route::get('/', function () { return view('welcome'); }); Auth::routes(); Route::get('/home', [HomeController::class, 'index'])->name('home'); Route::group(['middleware' => ['auth']], function() { Route::resource('roles', RoleController::class); Route::resource('users', UserController::class); Route::resource('products', ProductController::class); });
Step 9 – Create Controllers UserController, ProductController and RoleController
Controller by default we can create by the command of PHP Artisan as below:
php artisan make:controller name-of-controller
But we want to deploy also defaults methods, such as index(), store(), show(), edit(), update() and destroy() we need to run the command as below:
php artisan make:controller name-of-controller --resource
So lets create for each case php artisan make:controller UserController --resource
php artisan make:controller ProductController --resource
php artisan make:controller RoleController --resource
All the files are located to app/http/Controllers/name-of-controller.php
app/Http/Controllers/UserController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Models\User; use Spatie\Permission\Models\Role; use DB; use Hash; use Illuminate\Support\Arr; class UserController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index(Request $request) { $data = User::orderBy('id','DESC')->paginate(5); return view('users.index',compact('data')) ->with('i', ($request->input('page', 1) - 1) * 5); } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { $roles = Role::pluck('name','name')->all(); return view('users.create',compact('roles')); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $this->validate($request, [ 'name' => 'required', 'email' => 'required|email|unique:users,email', 'password' => 'required|same:confirm-password', 'roles' => 'required' ]); $input = $request->all(); $input['password'] = Hash::make($input['password']); $user = User::create($input); $user->assignRole($request->input('roles')); return redirect()->route('users.index') ->with('success','User created successfully'); } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { $user = User::find($id); return view('users.show',compact('user')); } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit($id) { $user = User::find($id); $roles = Role::pluck('name','name')->all(); $userRole = $user->roles->pluck('name','name')->all(); return view('users.edit',compact('user','roles','userRole')); } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { $this->validate($request, [ 'name' => 'required', 'email' => 'required|email|unique:users,email,'.$id, 'password' => 'same:confirm-password', 'roles' => 'required' ]); $input = $request->all(); if(!empty($input['password'])){ $input['password'] = Hash::make($input['password']); }else{ $input = Arr::except($input,array('password')); } $user = User::find($id); $user->update($input); DB::table('model_has_roles')->where('model_id',$id)->delete(); $user->assignRole($request->input('roles')); return redirect()->route('users.index') ->with('success','User updated successfully'); } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { User::find($id)->delete(); return redirect()->route('users.index') ->with('success','User deleted successfully'); } }
app/Http/Controllers/ProductController.php
<?php namespace App\Http\Controllers; use App\Models\Product; use Illuminate\Http\Request; class ProductController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ function __construct() { $this->middleware('permission:product-list|product-create|product-edit|product-delete', ['only' => ['index','show']]); $this->middleware('permission:product-create', ['only' => ['create','store']]); $this->middleware('permission:product-edit', ['only' => ['edit','update']]); $this->middleware('permission:product-delete', ['only' => ['destroy']]); } /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { $products = Product::latest()->paginate(5); return view('products.index',compact('products')) ->with('i', (request()->input('page', 1) - 1) * 5); } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { return view('products.create'); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { request()->validate([ 'name' => 'required', 'detail' => 'required', ]); Product::create($request->all()); return redirect()->route('products.index') ->with('success','Product created successfully.'); } /** * Display the specified resource. * * @param \App\Product $product * @return \Illuminate\Http\Response */ public function show(Product $product) { return view('products.show',compact('product')); } /** * Show the form for editing the specified resource. * * @param \App\Product $product * @return \Illuminate\Http\Response */ public function edit(Product $product) { return view('products.edit',compact('product')); } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param \App\Product $product * @return \Illuminate\Http\Response */ public function update(Request $request, Product $product) { request()->validate([ 'name' => 'required', 'detail' => 'required', ]); $product->update($request->all()); return redirect()->route('products.index') ->with('success','Product updated successfully'); } /** * Remove the specified resource from storage. * * @param \App\Product $product * @return \Illuminate\Http\Response */ public function destroy(Product $product) { $product->delete(); return redirect()->route('products.index') ->with('success','Product deleted successfully'); } }
app/Http/Controllers/RoleController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Permission; use DB; class RoleController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ function __construct() { $this->middleware('permission:role-list|role-create|role-edit|role-delete', ['only' => ['index','store']]); $this->middleware('permission:role-create', ['only' => ['create','store']]); $this->middleware('permission:role-edit', ['only' => ['edit','update']]); $this->middleware('permission:role-delete', ['only' => ['destroy']]); } /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index(Request $request) { $roles = Role::orderBy('id','DESC')->paginate(5); return view('roles.index',compact('roles')) ->with('i', ($request->input('page', 1) - 1) * 5); } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { $permission = Permission::get(); return view('roles.create',compact('permission')); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $this->validate($request, [ 'name' => 'required|unique:roles,name', 'permission' => 'required', ]); $role = Role::create(['name' => $request->input('name')]); $role->syncPermissions($request->input('permission')); return redirect()->route('roles.index') ->with('success','Role created successfully'); } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { $role = Role::find($id); $rolePermissions = Permission::join("role_has_permissions","role_has_permissions.permission_id","=","permissions.id") ->where("role_has_permissions.role_id",$id) ->get(); return view('roles.show',compact('role','rolePermissions')); } /** * Show the form for editing the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function edit($id) { $role = Role::find($id); $permission = Permission::get(); $rolePermissions = DB::table("role_has_permissions")->where("role_has_permissions.role_id",$id) ->pluck('role_has_permissions.permission_id','role_has_permissions.permission_id') ->all(); return view('roles.edit',compact('role','permission','rolePermissions')); } /** * Update the specified resource in storage. * * @param \Illuminate\Http\Request $request * @param int $id * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { $this->validate($request, [ 'name' => 'required', 'permission' => 'required', ]); $role = Role::find($id); $role->name = $request->input('name'); $role->save(); $role->syncPermissions($request->input('permission')); return redirect()->route('roles.index') ->with('success','Role updated successfully'); } /** * Remove the specified resource from storage. * * @param int $id * @return \Illuminate\Http\Response */ public function destroy($id) { DB::table("roles")->where('id',$id)->delete(); return redirect()->route('roles.index') ->with('success','Role deleted successfully'); } }
Step 10 – Create Blade Files
resources/views/layouts/app.blade.php
<html lang="{{ app()->getLocale() }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel 8 User Roles and Permissions Tutorial') }}</title> <!-- Scripts --> <script src="{{ asset('js/app.js') }}" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="https://fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css"> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> <nav class="navbar navbar-expand-md navbar-light navbar-laravel"> <div class="container"> <a class="navbar-brand" href="{{ url('/') }}"> Laravel 8 User Roles and Permissions - ItSolutionStuff.com </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <!-- Left Side Of Navbar --> <ul class="navbar-nav mr-auto"></ul> <!-- Right Side Of Navbar --> <ul class="navbar-nav ml-auto"> <!-- Authentication Links --> @guest <li><a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a></li> <li><a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a></li> @else <li><a class="nav-link" href="{{ route('users.index') }}">Manage Users</a></li> <li><a class="nav-link" href="{{ route('roles.index') }}">Manage Role</a></li> <li><a class="nav-link" href="{{ route('products.index') }}">Manage Product</a></li> <li class="nav-item dropdown"> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> {{ Auth::user()->name }} <span class="caret"></span> </a> <div class="dropdown-menu" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> {{ __('Logout') }} </a> <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;"> @csrf </form> </div> </li> @endguest </ul> </div> </div> </nav> <main class="py-4"> <div class="container"> @yield('content') </div> </main> </div> </body> </html>
resources/views/users/index.blade.php
@extends('layouts.app') @section('content') <div class="row"> <div class="col-lg-12 margin-tb"> <div class="pull-left"> <h2>Users Management</h2> </div> <div class="pull-right"> <a class="btn btn-success" href="{{ route('users.create') }}"> Create New User</a> </div> </div> </div> @if ($message = Session::get('success')) <div class="alert alert-success"> <p>{{ $message }}</p> </div> @endif <table class="table table-bordered"> <tr> <th>No</th> <th>Name</th> <th>Email</th> <th>Roles</th> <th width="280px">Action</th> </tr> @foreach ($data as $key => $user) <tr> <td>{{ ++$i }}</td> <td>{{ $user->name }}</td> <td>{{ $user->email }}</td> <td> @if(!empty($user->getRoleNames())) @foreach($user->getRoleNames() as $v) <label class="badge badge-success">{{ $v }}</label> @endforeach @endif </td> <td> <a class="btn btn-info" href="{{ route('users.show',$user->id) }}">Show</a> <a class="btn btn-primary" href="{{ route('users.edit',$user->id) }}">Edit</a> {!! Form::open(['method' => 'DELETE','route' => ['users.destroy', $user->id],'style'=>'display:inline']) !!} {!! Form::submit('Delete', ['class' => 'btn btn-danger']) !!} {!! Form::close() !!} </td> </tr> @endforeach </table> {!! $data->render() !!} <p class="text-center text-primary"><small>Tutorial by ItSolutionStuff.com</small></p> @endsection