Phân quyền trong laravel với spatie laravel permission

phân quyền trong laravel với spatie laravel permisison
5/5 - (4 votes)

Phân quyền là chủ đề khó nhằn nhất trong thiết kế website. Hôm nay chúng ta sẽ cùng tìm hiểu cách phân quyền trong larvel với sự hỗ trợ của của spatie.

Cài đặt laravel để tiến hành phân quyền trong laravel

Khi tiến hành phân quyền có nghĩa là chúng ta sẽ bắt đầu một dự án mới. Vì vậy nên Chúng ta sẽ bắt đầu với việc cài đặt laravel.

composer create-project --prefer-dist laravel/laravel phanquyen-app

Cài đặt spatie laravel permission để phân quyền

Để cài đặt gói spatie laravel permission vào dự án laravel nhằm phân quyền, bạn mở command lên và chuyển về thư mục gốc của dự án rồi chạy đoạn code dưới đây.

composer require spatie/laravel-permission

Trong bài hướng dẫn này có một số phần sử dụng form collection vì vậy bạn cần cài thêm gói form collection để hỗ trợ. Nếu bạn không muốn sử dụng nó thì có thể bỏ qua, và nhớ chỉnh lại một vài đoạn code cho phù hợp.

composer require laravelcollective/html

Tiếp theo bạn vào thư mục config và mở file app.php lên rồi thêm vào bên trong đó đoạn code như sau.

config/app.php

'providers' => [

	....

	Spatie\Permission\PermissionServiceProvider::class,
],

Sau khi đã chỉnh sửa như trên thì bạn cần chạy đoạn lệnh bên dưới để kích hoạt nó.

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

Và tiếp theo là chạy migrate với câu lệnh bên dưới để tạo ra các bảng như users, permissions, roles, role_has_permissions, model_has_permissions, model_has_roles.

php artisan migrate
table phân quyền trong laravel
Migrate tạo bảng cho phân quyền trong laravel

Các bước tiến hành phân quyền trong laravel

Bước 1: Đăng ký Middleware để phân quyền

Spatie cung cấp phân quyền trong laravel dưới dạng middleware vì vậy chúng ta cần thêm middleware vào file kernel.php đoạn code bên dưới.

....
protected $routeMiddleware = [
    ....
    'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
    'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
    'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
]
....

Bước 2: Tạo trang đăng nhập và đăng ký trong laravel

Ở bước bạn sẽ tạo giao diện đăng nhập và đăng ký mặc định cho laravel. Bạn thiết lập authentication cho trong laravel bằng cách chạy lần lượt từng dòng code ở dưới.

composer require laravel/ui
php artisan ui bootstrap --auth
npm install
npm run dev

Nếu gặp lỗi thì có thể chạy như bên dưới.

composer require laravel/ui "^3.0" 
php artisan ui bootstrap --auth
npm install
npm run dev

Thay vì bootstrap thì tại đây bạn cũng có thể sử dụng vue.

Bước 3: Thêm routes

Bạn cần thêm một số thông tin ở file web.php nhằm giúp cho laravel xác định đường đi (route) cho các trang con mà bạn sắp tạo.

routes/web.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\PermissionController;

...

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');

Route::group(['middleware' => ['auth']], function() {
    Route::resource('roles', RoleController::class);
    Route::resource('users', UserController::class);
    Route::resource('permissions', PermissionController::class);
});

Bước 4: Tạo controllers

Ta sẽ tiến hành tạo ra 3 file controllers lần lượt là: UserController.php, RoleController.php và PermissionController.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
{
    function __construct()
    {
         $this->middleware('permission:user-list|user-create|user-edit|user-delete', ['only' => ['index','store']]);
         $this->middleware('permission:user-create', ['only' => ['create','store']]);
         $this->middleware('permission:user-edit', ['only' => ['edit','update']]);
         $this->middleware('permission:user-delete', ['only' => ['destroy']]);
    }
    public function index(Request $request)
    {

        $users = User::orderBy('id','DESC')->paginate(5);

        return view('users.index',compact('users'));

    }
    

    public function create()
    {
        if(auth()->user()->hasRole('Super-Admin')){
            $roles = Role::pluck('name','name')->all();
        }else{
            $roles = Role::pluck('name','name')->except(['name', 'Super-Admin']);
        }
        
        return view('users.create',compact('roles'));
    }
    

    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required',
            'email' => 'required|email|unique:users,email',
            'password' => 'required',
            'confirm-password' => 'required|same:password',
            'roles' => 'required'
        ]);
    
        $input = $request->all();
        $input['password'] = Hash::make($input['password']);
    
        $user = User::create($input);
        $user->assignRole($request->input('roles'));
    
        $notification = array(            
            'message' => 'User created successfully',
            'alert-type' => 'success'            
        );
        return redirect()->route('users.index')
                        ->with($notification);
    }
    
    public function show($id)
    {
        return redirect()->route('users.index');
    }
    
    public function edit($id)
    {
        $user = User::find($id);
        if($user->hasRole('Super-Admin')){
            $notification = array(            
                'message' => "You have no permission for edit this user",
                'alert-type' => 'error'            
            );
            return redirect()->route('users.index')
                            ->with($notification);
        }
        if(auth()->user()->hasRole('Super-Admin')){
            $roles = Role::pluck('name','name')->all();
        }else{
            $roles = Role::pluck('name','name')->except(['name', 'Super-Admin']);
        }
        $userRole = $user->roles->pluck('name','name')->all();
    
        return view('users.edit',compact('user','roles','userRole'));
    }

    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'));
    
        $notification = array(            
            'message' => 'User updated successfully',
            'alert-type' => 'success'            
        );
        return redirect()->route('users.index')
                        ->with($notification);
    }
    
    public function destroy($id)
    {
        $user = User::find($id);
        if(auth()->id() == $id){
            $notification = array(            
                'message' => "You cannot delete yourself",
                'alert-type' => 'error'            
            );
            return redirect()->route('users.index')
                            ->with($notification);
        }
        if($user->hasRole('Super-Admin')){
            $notification = array(            
                'message' => "You have no permission for delete this user",
                'alert-type' => 'error'            
            );
            return redirect()->route('users.index')
                            ->with($notification);
        }
        $user->delete();
        $notification = array(            
            'message' => "User deleted successfully",
            'alert-type' => 'success'            
        );
        return redirect()->route('users.index')
                        ->with($notification);
    }    
}

app/Http/Controllers/RoleController.php

<?php

namespace App\Http\Controllers;


use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Http\Controllers\Controller;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use Illuminate\Support\Facades\Auth;
use DB;
    
class RoleController extends Controller
{

    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']]);
    }
    
    public function index(Request $request)
    {
        $roles = Role::orderBy('id','DESC')->paginate(5);
        return view('roles.index',compact('roles'));
    }
    
    public function create()
    {
        $permission = Permission::get();
        return view('roles.create',compact('permission'));
    }
    
    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');
    }

    public function show($id)
    {
        return redirect()->route('roles.index');
    }
    
    public function edit($id)
    {
        $role = Role::find($id);
        if($role->name == 'Super-Admin'){
            $notification = array(            
                'message' => "You have no permission for edit this role",
                'alert-type' => 'error'            
            );
            return redirect()->route('roles.index')
                            ->with($notification);
        }

        $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'));
    }

    public function update(Request $request, $id)
    {
        $this->validate($request, [
            'name' => [
                'required',
                Rule::unique('roles','name')->ignore($id)
            ],
            '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');
    }

    public function destroy($id)
    {
        $role = Role::find($id);

        if (auth()->user()->roles->find($id)) {        
            $notification = array(            
                'message' => 'You have no permission for delete this role',
                'alert-type' => 'error'            
            );
            return redirect()->route('roles.index')
                            ->with($notification);
        }
        if ($role->name == "Super-Admin"){
            $notification = array(            
                'message' => 'You have no permission for delete Super-Admin role',
                'alert-type' => 'error'            
            );
            return redirect()->route('roles.index')
                            ->with($notification);
        }
        $role->delete();       

        $notification = array(            
            'message' => 'The role deleted successfully',
            'alert-type' => 'success'            
        );
        return redirect()->route('roles.index')
                        ->with($notification);
    }
}

app/Http/Controllers/PermisisonController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Http\Controllers\Controller;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use PhpParser\Node\Stmt\TryCatch;


use DB;

class PermissionController extends Controller
{

    function __construct()
    {
         
         $this->middleware('role:Super-Admin');
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\View\View
     */
    public function index(Request $request)
    {
        $permissions = Permission::all();
        if ($request->ajax()) {
            return response()->json([
                'permissions'=>$permissions,
            ]);
        }
        return view('permissions.index');

    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\View\View
     */
    public function create()
    {
        return redirect()->route('permissions.index');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function store(Request $request)
    {            
        
        $this->validate($request, [
            'name' => 'required|unique:permissions',
        ]);
        Permission::Create(
            [
                'name' => $request->name,
            ]
        );

        $notification = array(            
            'message' => 'Permission added successfully',
            'alert-type' => 'success'            
        );
        
        return redirect()->route('permissions.index')
                        ->with($notification);

    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     *
     * @return \Illuminate\View\View
     */
    public function show($id)
    {
         return redirect()->route('permissions.index');
    }

    public function edit($id)
    {
        return redirect()->route('permissions.index');
    }

    public function update(Request $request, $id)
    {
        $this->validate($request, [
            'name' => [
                'required',
                Rule::unique('permissions','name')->ignore($id)
            ]
        ]);
        $permission = Permission::find($id); 
        $permission->name = $request->name;        
        $permission->save();
    
        return redirect()->route('permissions.index')
                        ->with('success','Permission updated successfully');
    }

    public function destroy($id)
    {
        DB::table("permissions")->where('id',$id)->delete();        
        return response()->json([
            'message' => 'Record deleted successfully!'
          ]);
        /* $notification = array(            
            'message' => 'Permissions deleted successfully',
            'alert-type' => 'success'            
        );
        return redirect()->route('permissions.index')
                        ->with($notification); */

    }
}

Bước 5: Tạo layouts

Sau khi tiến hành tạo controller xong thì chúng ta cần phải tạo ra các file blade.php để có giao diện quản lý như index, create, edit, show.

Trước hết ta sẽ xây dựng layout cho dự án của mình bằng cách tạo một thư mục layouts trong views và lần lượt thêm các file dưới đây. Và trong dự án này mình sử dụng thư viện dashboard miễn phí AdminLTE.io để làm layout.

resources/views/layouts/app.blade.php

<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <title>@yield('title')</title>

    <meta name="csrf_token" content="{{ csrf_token() }}" />

    @include('layouts.styles')

    <script>
        var BASE_URL = '{{ url("/") }}';
    </script>

</head>
<body class="hold-transition sidebar-mini layout-fixed">

    <div class="wrapper">
        @include('layouts.header')
        @include('layouts.sidebar')

        <!-- Content Wrapper -->
        <div class="content-wrapper">
            <!-- Content Header (Page header) -->
            <div class="content-header">
                <div class="container-fluid">
                    <div class="row mb-2">
                        <div class="col-sm-6">
                            @yield('breadcrumb')
                        </div><!-- /.col -->
                        <div class="col-sm-6">
                            <ol class="breadcrumb float-sm-right">
                                <li class="breadcrumb-item"><a href="#">Home</a></li>
                                <li class="breadcrumb-item active">Dashboard v1</li>
                            </ol>
                        </div><!-- /.col -->
                    </div><!-- /.row -->
                </div><!-- /.container-fluid -->
            </div>
            <!-- /.content-header -->

            <!-- Main content -->
            <div class="content">
                <div class="container-fluid">
                    <div class="row">
                        @yield('content')
                    </div>
                    <!-- /.row -->
                </div><!-- /.container-fluid -->
            </div>
            <!-- /.content -->
        </div>
        <footer class="main-footer">
            <strong>Copyright &copy; 2014-2021 <a href="https://adminlte.io">AdminLTE.io</a>.</strong>
            All rights reserved.
            <div class="float-right d-none d-sm-inline-block">
            <b>Version</b> 3.2.0
            </div>
        </footer>
    </div>

    @include('layouts.footer')
    @yield('scripts')
</body>
</html>

resources/views/layouts/header.blade.php

<!-- Navbar -->
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
    <!-- Left navbar links -->
    <ul class="navbar-nav">
      <li class="nav-item">
        <a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a>
      </li>
      <li class="nav-item d-none d-sm-inline-block">
        <a href="index3.html" class="nav-link">Home</a>
      </li>
      <li class="nav-item d-none d-sm-inline-block">
        <a href="#" class="nav-link">Contact</a>
      </li>
    </ul>

    <!-- Right navbar links -->
    <ul class="navbar-nav ml-auto align-items-center">
      <!-- Navbar Search -->
      <li class="nav-item">
            <!-- SEARCH FORM -->
            <form class="form-inline ml-0 ml-md-3">
                <div class="input-group input-group-sm">
                    <input class="form-control form-control-navbar" type="search" placeholder="Search" aria-label="Search">
                    <div class="input-group-append">
                    <button class="btn btn-navbar" type="submit">
                        <i class="fa fa-search"></i>
                    </button>
                    </div>
                </div>
            </form>
        </li>

        <!-- Notifications Dropdown Menu -->
      <li class="nav-item dropdown">
        <a class="nav-link" data-toggle="dropdown" href="#">
          <i class="far fa-bell"></i>
          <span class="badge badge-warning navbar-badge">15</span>
        </a>
        <div class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
          <span class="dropdown-item dropdown-header">15 Notifications</span>
          <div class="dropdown-divider"></div>
          <a href="#" class="dropdown-item">
            <i class="fas fa-envelope mr-2"></i> 4 new messages
            <span class="float-right text-muted text-sm">3 mins</span>
          </a>
          <div class="dropdown-divider"></div>
          <a href="#" class="dropdown-item">
            <i class="fas fa-users mr-2"></i> 8 friend requests
            <span class="float-right text-muted text-sm">12 hours</span>
          </a>
          <div class="dropdown-divider"></div>
          <a href="#" class="dropdown-item">
            <i class="fas fa-file mr-2"></i> 3 new reports
            <span class="float-right text-muted text-sm">2 days</span>
          </a>
          <div class="dropdown-divider"></div>
          <a href="#" class="dropdown-item dropdown-footer">See All Notifications</a>
        </div>
      </li>
      <li class="nav-item">
        <a class="nav-link" data-widget="fullscreen" href="#" role="button">
          <i class="fas fa-expand-arrows-alt"></i>
        </a>
      </li>
      <li class="nav-item dropdown dropdown-user">
            <a class="nav-link dropdown-user-link" id="dropdown-user" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
                <div class="user-nav d-sm-flex d-none">
                    <span class="user-name font-weight-bolder dropdown-toggle ">{{ Auth::user()->name }}</span>
                </div>
            </a>
            <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdown-user">
                <a class="dropdown-item" href="#"><i class="fas fa-user-o"></i>  Profile</a>
                <a class="dropdown-item" href="#"><i class="fas fa-envelope-o"></i>  Inbox</a>
                <a class="dropdown-item" href="#"><i class="fas fa-check-square-o"></i>  Task</a>
                <a class="dropdown-item" href="#"><i class="fas fa-comment-o"></i>  Chats</a>
                <div class="dropdown-divider"></div>
                <a class="dropdown-item" href="#"><i class="fas fa-cog"></i>  Settings</a>
                <a class="dropdown-item" href="#"><i class="fas fa-question-circle-o"></i>  FAQ</a>
                <a class="dropdown-item" href="{{ route('logout') }}"
                    onclick="event.preventDefault();
                                document.getElementById('logout-form').submit();">
                                <i class="fas fa-power-off"></i>
                    {{ ('Logout') }}
                </a>
                <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                    @csrf
                </form>

            </div>
        </li>
    </ul>
  </nav>
  <!-- /.navbar -->

resources/views/layouts/footer.blade.php

<!-- jQuery -->
<script src="{{ asset('plugins/jquery/jquery.min.js') }}"></script>
<!-- jQuery UI 1.11.4 -->
<script src="{{ asset('plugins/jquery-ui/jquery-ui.min.js') }}"></script>
<!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
<script>
  $.widget.bridge('uibutton', $.ui.button)
</script>
<!-- Bootstrap 4 -->
<script src="{{ asset('plugins/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
<!-- datepicker -->
<script src="{{ asset('plugins/moment/moment.min.js') }}"></script>
<!-- Tempusdominus Bootstrap 4 -->
<script src="{{ asset('plugins/tempusdominus-bootstrap-4/js/tempusdominus-bootstrap-4.min.js') }}"></script>
<!-- Select2 -->
<script src="{{ asset('plugins/select2/js/select2.full.min.js') }}"></script>
<!-- DataTable -->
<script src="{{ asset('plugins/DataTables/datatables.min.js')}}"></script>
<script src="{{ asset('plugins/DataTables/datatables.moment.min.js')}}"></script>
<!-- sweetalert2 -->
<script src="{{ asset('plugins/sweetalert2/sweetalert2.min.js') }}"></script>

<!-- AdminLTE App -->
<script src="{{ asset('js/app.min.js') }}"></script>
<script>
     @if(Session::has('alert-type'))
        var type = "{{ Session::get('alert-type', 'info') }}";
        var content = "{{ Session::get('message', 'info') }}";
        var Toast = Swal.mixin({
        toast: true,
        position: 'top-end',
        showConfirmButton: false,
        timer: 3000
        });
        switch(type){
            case 'info':
                Toast.fire({
                icon: 'info',
                title: content
                })
                break;
            case 'warning':
                Toast.fire({
                icon: 'warning',
                title: content
                })
                break;
            case 'success':
                Toast.fire({
                icon: 'success',
                title: content

                })
                break;
            case 'error':
                Toast.fire({
                icon: 'error',
                title: content
                })
                break;
        }
    @endif
    </script>

resources/views/layouts/sidebar.blade.php

<aside class="main-sidebar sidebar-dark-default elevation-4">
    <!-- Brand Logo -->
    <a href="index3.html" class="brand-link">
      <!-- <img src="{{asset('img/AdminLTELogo.png') }}" alt="AdminLTE Logo" class="brand-image img-circle elevation-3" style="opacity: .8"> -->
      <span class="brand-text font-weight-light">Employee</span>
    </a>

    <!-- Sidebar -->
    <div class="sidebar">      
      <!-- Sidebar Menu -->
      <nav class="mt-2">
        <ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
          <!-- Add icons to the links using the .nav-icon class
               with font-awesome or any other icon font library -->
          <li class="nav-item menu-open">
            <a href="#" class="nav-link active">
              <i class="nav-icon fas fa-tachometer-alt"></i>
              <p>
                Dashboard
                <i class="right fas fa-angle-left"></i>
              </p>
            </a>
            <ul class="nav nav-treeview">
              <li class="nav-item">
                <a href="./index.html" class="nav-link active">
                  <i class="far fa-circle nav-icon"></i>
                  <p>Dashboard v1</p>
                </a>
              </li>
              <li class="nav-item">
                <a href="./index2.html" class="nav-link">
                  <i class="far fa-circle nav-icon"></i>
                  <p>Dashboard v2</p>
                </a>
              </li>
            </ul>
          </li>
          <li class="nav-item">
            <a href="pages/widgets.html" class="nav-link">
              <i class="nav-icon fas fa-th"></i>
              <p>
                Widgets
                <span class="right badge badge-danger">New</span>
              </p>
            </a>
          </li>
          <li class="nav-item">
            <a href="#" class="nav-link">
              <i class="nav-icon fas fa-chart-pie"></i>
              <p>
                Charts
                <i class="right fas fa-angle-left"></i>
              </p>
            </a>
            <ul class="nav nav-treeview">
              <li class="nav-item">
                <a href="pages/charts/chartjs.html" class="nav-link">
                  <i class="far fa-circle nav-icon"></i>
                  <p>ChartJS</p>
                </a>
              </li>
              <li class="nav-item">
                <a href="pages/charts/flot.html" class="nav-link">
                  <i class="far fa-circle nav-icon"></i>
                  <p>Flot</p>
                </a>
              </li>
              <li class="nav-item">
                <a href="pages/charts/inline.html" class="nav-link">
                  <i class="far fa-circle nav-icon"></i>
                  <p>Inline</p>
                </a>
              </li>
              <li class="nav-item">
                <a href="pages/charts/uplot.html" class="nav-link">
                  <i class="far fa-circle nav-icon"></i>
                  <p>uPlot</p>
                </a>
              </li>
            </ul>
          </li>
          <li class="nav-item">          
            <a href="{{ route('schedules.index') }}" class="nav-link">
              <i class="fas fa-calendar-alt nav-icon"></i>
              <p>
                Schedule
              </p>
            </a>
          </li>
          <li class="nav-item">          
            <a href="{{ route('attendances.index') }}" class="nav-link">
              <i class="fas fa-clock nav-icon"></i>
              <p>
                Attendance
              </p>
            </a>
          </li>
          <li class="nav-item">
            <a href="#" class="nav-link">
               <i class="fas fa-users nav-icon"></i>
              <p>
                Users
                <i class="fas fa-angle-left right"></i>
              </p>
            </a>
            <ul class="nav nav-treeview">
              <li class="nav-item">
                <a href="{{ route('users.index') }}" class="nav-link">
                  <i class="far fa-circle nav-icon"></i>
                  <p>User</p>
                </a>
              </li>
              <li class="nav-item">
                <a href="{{ route('roles.index') }}" class="nav-link">
                  <i class="far fa-circle nav-icon"></i>
                  <p>Role</p>
                </a>
              </li>
              <li class="nav-item">
              @can('isSuperAdmin')
                <a href="{{ route('permissions.index') }}" class="nav-link">
                  <i class="far fa-circle nav-icon"></i>
                  <p>Permission</p>
                </a>
                @endcan
              </li>
            </ul>
          </li>

        </ul>
      </nav>
      <!-- /.sidebar-menu -->
    </div>
    <!-- /.sidebar -->
  </aside>

resources/views/layouts/style.blade.php

<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">

<!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="{{ asset('/plugins/fontawesome-free/css/all.min.css') }}">
  <!-- Ionicons -->
  <link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css') }}">
  <!-- Theme style -->
  <link rel="stylesheet" href="{{ asset('css/app.min.css')  }}">
  <!-- Tempusdominus Bootstrap 4 -->
  <link rel="stylesheet" href="{{ asset('plugins/tempusdominus-bootstrap-4/css/tempusdominus-bootstrap-4.css') }}">
 <!-- Select2 -->
  <link rel="stylesheet" href="{{ asset('plugins/select2/css/select2.min.css') }}">
  <link rel="stylesheet" href="{{ asset('plugins/select2-bootstrap4-theme/select2-bootstrap4.min.css') }}">
  <!-- DataTable -->
  <link rel="stylesheet" href="{{ asset('plugins/DataTables/dataTables.css')}}">
  <!-- sweetalert2 -->
<link rel="stylesheet" href="{{ asset('plugins/sweetalert2/sweetalert2.min.css') }}">

@yield('styles')

Bước 6: Tạo giao diện quản lý users

Tiếp tục phần giao diện trong phần quyền laravel với spatie laravel permission ta tạo thư mục users để chứa các file index, create, edit.

giao diện users trong phân quyền trong laravel
giao diện quản lý users trong phân quyền laravel

resources/views/users/index.blade.php


@extends('layouts.app')
@section('page-title')
<h4 class="m-0">Users Management</h4>
@endsection
@section('content')


<div class="col-md-12">
  <div class="card">
    <div class="card-header">
      <h2 class="card-title">Users Management</h2>
      <div class="card-tools">
        @can('user-create')
        <a class="btn btn-success" href="{{ route('users.create') }}"><i class="fas fa-plus-square"></i> Add User</a>
        @endcan
      </div>
    </div>
    <!-- /.card-header -->
    <div class="card-body table-responsive">  
      <table class="table table-striped table-bordered">
      <tr class="bg-blue text-center">
        <th width="50px">No.</th>
        <th>Name</th>
        <th>Email</th>
        <th>Roles</th>
        <th width="150px">Action</th>
      </tr>
      @foreach ($users as $key => $user)
        <tr>
          <td class="text-center">{{ ++$key }}</td>
          <td>{{ $user->name }}</td>
          <td>{{ $user->email }}</td>
          <td class="text-center">
            @if(!empty($user->getRoleNames()))
              @foreach($user->getRoleNames() as $v)
                <label class="badge badge-success">{{ $v }}</label>
              @endforeach
            @endif
          </td>
          <td class="text-center"> 
            @can('user-edit')
            <a class="btn btn-sm btn-primary" href="{{ route('users.edit',$user->id) }}">Edit</a>
            @endcan            
            @can('user-delete')
                {!! Form::open(['method' => 'DELETE','route' => ['users.destroy', $user->id],'style'=>'display:inline']) !!}
                    {!! Form::submit('Delete', ['class' => 'btn btn-sm btn-danger delete_confirm' ]) !!}
                {!! Form::close() !!}
            @endcan
          </td>
        </tr>
      @endforeach
      </table>
    </div>
    <!-- /.card-body -->
    <div class="card-footer clearfix">
      <div class="float-left">
          <div class="dataTables_info">
              Showing {{ $users->firstItem() }} to {{ $users->lastItem() }} of {{ $users->total() }} entries
          </div>
      </div>
      <div class="float-right">
          {{ $users->links() }}
      </div>
    </div>
  </div>
  <!-- /.card -->
</div>
@endsection 
@section('scripts')
<script type="text/javascript">
$(function () {
    $('.delete_confirm').click(function(event) {
        var form =  $(this).closest("form");
        event.preventDefault();
        swal.fire({
            title: 'Are you sure you want to delete this record?',
            text: "If you delete this, it will be gone forever.",
            icon: 'warning',
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: 'Yes, delete it!',
            showDenyButton: true,
            denyButtonText: 'Cancel',
        })
        .then((result) => {
            if (result.isConfirmed) {
                form.submit();
             } else if (result.isDenied) {
                Swal.fire('Your record is safe', '', 'info')
            }
            
        });
    });
});
</script>
@endsection

resources/views/users/create.blade.php


@extends('layouts.app')
@section('breadcrumb')
<h4 class="m-0">Create New User</h4>
@endsection
@section('content')
<!-- general form elements -->
<div class="col-md-12">
    <div class="card card-default">
        <div class="card-header">
        <h2 class="card-title">Create New User</h2>
            <div class="card-tools">
                <a class="btn btn-success" href="{{ route('users.index') }}"><i class="fa fa-angle-double-left"></i> Back To User List</a>
            </div>
        </div>

        <!-- /.card-header -->
        <!-- form start -->
        {!! Form::open(array('route' => 'users.store','method'=>'POST')) !!}
        <div class="card-body">        
            <div class="row">
                <div class="col-md-6">
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Name:</strong>
                            {!! Form::text('name', null, array('value' => '{{ old("name") }}', 'placeholder' => 'Name','class' => 'form-control')) !!}
                            <span class="text-danger">{{ $errors->first('name') }}</span>
                        </div>
                    </div>
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Email:</strong>
                            {!! Form::text('email', null, array('value' => '{{ old("email") }}', 'placeholder' => 'Email','class' => 'form-control')) !!}
                            <span class="text-danger">{{ $errors->first('email') }}</span>
                        </div>
                    </div>
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Password:</strong>
                            {!! Form::password('password', array('placeholder' => 'Password','class' => 'form-control')) !!}
                            <span class="text-danger">{{ $errors->first('password') }}</span>
                        </div>
                    </div>
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Confirm Password:</strong>
                            {!! Form::password('confirm-password', array('placeholder' => 'Confirm Password','class' => 'form-control')) !!}
                            <span class="text-danger">{{ $errors->first('confirm-password') }}</span>
                        </div>
                    </div>

                </div>   
                <div class="col-md-6">
                    <div class="form-group">
                        <strong>Role:</strong>
                        {!! Form::select('roles[]', $roles,[], array('value' => '{{ old("roles") }}', 'class' => 'form-control','multiple')) !!}
                        <span class="text-danger">{{ $errors->first('roles') }}</span>
                    </div>
                </div>     
            </div>        
        </div>
        <!-- /.card-body -->

        <div class="card-footer">
            <button type="submit" class="btn btn-primary">Submit</button>        
        </div>
        {!! Form::close() !!}
    </div>
    <!-- /.card -->
</div>


@endsection

resources/views/users/edit.blade.php


@extends('layouts.app')
@section('breadcrumb')
<h4 class="m-0">Edit User</h4>
@endsection
@section('content')
<!-- general form elements -->
<div class="col-md-12">
    <div class="card card-default">
        <div class="card-header">
            <h2 class="card-title">Edit User</h2>
            <div class="card-tools">
                <a class="btn btn-success" href="{{ route('users.index') }}"><i class="fa fa-angle-double-left"></i>  Back To User List</a>
            </div>
        </div>
        <!-- /.card-header -->
        <!-- form start -->
        {!! Form::model($user, ['method' => 'PATCH','route' => ['users.update', $user->id]]) !!}
        <div class="card-body">        
            <div class="row">
                <div class="col-md-6">
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Name:</strong>
                            {!! Form::text('name', null, array('value' => '{{ old("name") }}', 'placeholder' => 'Name','class' => 'form-control')) !!}
                            <span class="text-danger">{{ $errors->first('name') }}</span>
                        </div>
                    </div>
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Email:</strong>
                            {!! Form::text('email', null, array('value' => '{{ old("email") }}', 'placeholder' => 'Email','class' => 'form-control')) !!}
                            <span class="text-danger">{{ $errors->first('email') }}</span>
                        </div>
                    </div>
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Password:</strong>
                            {!! Form::password('password', array('placeholder' => 'Password','class' => 'form-control')) !!}
                            <span class="text-danger">{{ $errors->first('password') }}</span>
                        </div>
                    </div>
                    <div class="col-xs-12 col-sm-12 col-md-12">
                        <div class="form-group">
                            <strong>Confirm Password:</strong>
                            {!! Form::password('confirm-password', array('placeholder' => 'Confirm Password','class' => 'form-control')) !!}
                            <span class="text-danger">{{ $errors->first('confirm-password') }}</span>
                        </div>
                    </div>
                </div>   
                <div class="col-md-6">
                    <div class="form-group">
                        <strong>Role:</strong>
                        {!! Form::select('roles[]', $roles,$userRole, array('class' => 'form-control','multiple')) !!}
                        <span class="text-danger">{{ $errors->first('roles') }}</span>
                    </div>
                </div>     
            </div>        
        </div>
        <!-- /.card-body -->

        <div class="card-footer">
            <button type="submit" class="btn btn-primary">Submit</button>        
        </div>
        {!! Form::close() !!}
    </div>
    <!-- /.card -->
</div>
@endsection

Bước 7: Tạo giao diện quản lý Roles

Tương tự như users thì ở đây chúng ta cũng tạo ra 3 file lần lượt là create, edit, index. Và dĩ nhiên chúng ta cũng không cần phải tạo file show vì nó thật sự không cần thiết.

giao diện roles trong phân quyền laravel
Giao diện Roles trong phân quyền laravel

resources/views/roles/index.blade.php

@extends('layouts.app')
@section('content')
<div class="col-md-12">
  <div class="card">
    <div class="card-header">
        <h2 class="card-title">Roles Management</h2>
        <div class="card-tools">
            <a class="btn btn-success" href="{{ route('roles.create') }}"><i class="fas fa-plus-square"></i> New Role</a>
        </div>
    </div>
    <!-- /.card-header -->
    <div class="card-body table-responsive">  
        <table class="table table-striped table-bordered">
            <tr class="bg-blue text-center">
                <th width="50px">No</th>
                <th>Name</th>
                <th width="150px">Action</th>
            </tr>
            @foreach ($roles as $key => $role)
            <tr>
                <td class="text-center">{{ ++$key }}</td>
                <td class="text-center">{{ $role->name }}</td>
                <td class="text-center">
                    
                    @can('role-edit')
                        <a class="btn btn-sm btn-primary" href="{{ route('roles.edit',$role->id) }}">Edit</a>
                    @endcan
                    @can('role-delete')
                        {!! Form::open(['method' => 'DELETE','route' => ['roles.destroy', $role->id],'style'=>'display:inline']) !!}
                            {!! Form::submit('Delete', ['class' => 'btn btn-sm btn-danger delete_confirm']) !!}
                        {!! Form::close() !!}
                    @endcan
                </td>
            </tr>
            @endforeach
        </table>

        {!! $roles->render() !!}
    </div>
    <!-- /.card-body -->
    <div class="card-footer clearfix">
        <div class="float-left">
            <div class="dataTables_info">
                Showing {{ $roles->firstItem() }} to {{ $roles->lastItem() }} of {{ $roles->total() }} entries
            </div>
        </div>
        <div class="float-right">
            {{ $roles->links() }}
        </div>
    </div>
  </div>
  <!-- /.card -->
</div>
@endsection 
@section('scripts')
<script type="text/javascript">
$(function () {
    $('.delete_confirm').click(function(event) {
        var form =  $(this).closest("form");
        event.preventDefault();
        swal.fire({
            title: 'Are you sure you want to delete this record?',
            text: "If you delete this, it will be gone forever.",
            icon: 'warning',
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: 'Yes, delete it!',
            showDenyButton: true,
            denyButtonText: 'Cancel',
        })
        .then((result) => {
            if (result.isConfirmed) {
                form.submit();
             } else if (result.isDenied) {
                Swal.fire('Your record is safe', '', 'info')
            }
            
        });
    });
});
</script>
@endsection

resources/views/roles/create.blade.php


@extends('layouts.app')
@section('content')
<!-- general form elements -->
<div class="col-md-12">
    <div class="card card-default">
        <div class="card-header">
            <h2 class="card-title">Create New Role</h2>
                <div class="card-tools">
                <a class="btn btn-success" href="{{ route('roles.index') }}"><i class="fas fa-angle-double-left"></i> Back To Role List</a>
            </div>
        </div>
        <!-- /.card-header -->
        <!-- form start -->
        {!! Form::open(array('route' => 'roles.store','method'=>'POST')) !!}
        <div class="card-body">          

            <div class="tab-pane" id="settings">
                <div class="form-horizontal">
                    <div class="form-group">
                        @if (count($errors) > 0)
                        <div class="alert alert-danger">
                            <strong>Whoops!</strong> There were some problems with your input.<br>
                            <ul>
                            @foreach ($errors->all() as $error)
                                <li>{{ $error }}</li>
                            @endforeach
                            </ul>
                        </div>
                        @endif
                    </div>
                    <div class="form-group row">
                        <label for="inputName" class="col-md-2 col-form-label">Name</label>
                        <div class="col-md-10">
                            {!! Form::text('name', null, array('placeholder' => 'Name','class' => 'form-control')) !!}
                    
                        </div>
                    </div>
                    <hr>
                    <div class="form-group row">
                        <label for="inputPermission" class="col-md-2 col-form-label">Permission</label>
                        <div class="col-md-10">
                            
                            <div class="row">                            
                                <?php 
                                    $abc ="";
                                    $len = count($permission);     
                                ?>
                                @foreach($permission as $key => $value)
                                
                                    <?php 
                                    
                                        if ($key === 0) {
                                            echo '<div class="col-lg-4">';
                                        }                                
                                        
                                        if ($abc != substr($value->name,0,strpos($value->name,"-")) && $key === 0){
                                            $abc = substr($value->name,0,strpos($value->name,"-"));
                                            
                                            echo '<label>'.$abc. '</label><div class="block">';

                                        }  else if($abc != substr($value->name,0,strpos($value->name,"-")) && $key !== 0){
                                            $abc = substr($value->name,0,strpos($value->name,"-"));
                                            echo '</div></div><div class="col-lg-4">';
                                            echo '<label>'.$abc. '</label><div class="block">';
                                        }  
                                            
                                    ?>
                                    {{ Form::checkbox('permission[]', $value->id, false, array('class' => 'name')) }}
                                    {{ $value->name }}
                                    <br />
                                    <?php
                                        if ($key === $len-1) {
                                            echo '</div></div>';
                                        }
                                    ?>
                                @endforeach
                            </div>
                        </div>
                    </div>
                    
                </div>
            </div>
            <!-- /.tab-pane -->
            
        </div>
        <!-- /.card-body -->

        <div class="card-footer">
            <button type="submit" class="btn btn-primary">Submit</button>        
        </div>
        {!! Form::close() !!}
    </div>
    <!-- /.card -->
</div>
@endsection 

resources/views/roles/edit.blade.php


@extends('layouts.app')

@section('breadcrumb')
<h4 class="m-0">Edit Role</h4>
@endsection
@section('content')
<!-- general form elements -->
<div class="col-md-12">
    <div class="card card-default">
        <div class="card-header">
            <h2 class="card-title">Edit roles</h2>
            <div class="card-tools">
                <a class="btn btn-success" href="{{ route('roles.index') }}"><i class="fas fa-angle-double-left"></i> Back To Role List</a>
            </div>
    </div>
        <!-- /.card-header -->
        <!-- form start -->
        {!! Form::model($role, ['method' => 'PATCH','route' => ['roles.update', $role->id]]) !!}
        <div class="card-body"> 
            
        <div class="tab-pane" id="settings">
                <div class="form-horizontal">
                    <div class="form-group">
                        @if (count($errors) > 0)
                        <div class="alert alert-danger">
                            <strong>Whoops!</strong> There were some problems with your input.<br>
                            <ul>
                            @foreach ($errors->all() as $error)
                                <li>{{ $error }}</li>
                            @endforeach
                            </ul>
                        </div>
                        @endif
                    </div>
                    <div class="form-group row">
                        <label for="inputName" class="col-md-2 col-form-label">Name</label>
                        <div class="col-md-10">
                            {!! Form::text('name', null, array('placeholder' => 'Name','class' => 'form-control')) !!}
                    
                        </div>
                    </div>
                    <hr>
                    <div class="form-group row">
                        <label for="inputPermission" class="col-md-2 col-form-label">Permission</label>
                        <div class="col-md-10">
                            
                            <div class="row">                            
                            <?php 
                                $abc ="";
                                $len = count($permission);
                            ?>
                            @foreach($permission as $key => $value)
                                <?php 
                                    if ($key === 0) {
                                        echo '<div class="col-md-4">';
                                    }                                
                                    
                                    if ($abc != substr($value->name,0,strpos($value->name,"-")) && $key === 0){
                                        $abc = substr($value->name,0,strpos($value->name,"-"));
                                        
                                        echo '<label>'.$abc. '</label><div class="block">';

                                    }  else if($abc != substr($value->name,0,strpos($value->name,"-")) && $key !== 0){
                                        $abc = substr($value->name,0,strpos($value->name,"-"));
                                        echo '</div></div><div class="col-md-4">';
                                        echo '<label>'.$abc. '</label><div class="block">';
                                    }                    
                                ?>
                                {{ Form::checkbox('permission[]', $value->id, in_array($value->id, $rolePermissions) ? true : false, array('class' => 'name')) }}
                                {{ $value->name }}
                                <br/>
                                <?php
                                    if ($key === $len-1) {
                                        echo '</div></div>';
                                    }
                                ?>
                            @endforeach
                            </div>
                        </div>
                    </div>
                    
                </div>
            </div>
            <!-- /.tab-pane -->



                  
        </div>
        <!-- /.card-body -->

        <div class="card-footer">
            <button type="submit" class="btn btn-primary">Submit</button>        
        </div>
        {!! Form::close() !!}
    </div>
    <!-- /.card -->
</div>


@endsection

Trong phân quyền cho laravel với spatie laravel permissions thì cái danh sách permission ở role sẽ dài ngoằng không đẹp, nên mình đã tạo group cho nó bằng cách sử dụng cụm từ trước dấu gạch ngang (-) để làm label. Nên khi tạo permissions bạn cần phải tạo cho đúng rule để có kết quả như mình đã làm.

Bước 7: Tạo giao diện quản lý cho permissions

Ở một số chia sẽ trên mạng về hướng dẫn phân quyền trong laravel thường sẽ không hướng dẫn tạo giao diện quản lý cho permission cũng như permission controller. Nhưng ở đây mình muốn chia sẽ luôn để khi tạo permissions mới thì mọi người có thể tạo luôn mà không cần vào database hay chạy seeder.

Và ở phần này chúng ta chỉ cần tạo 1 file index.blade.php thôi vì edit và create tôi đã sử dụng modal form để thay thế.

giao diện permission trong phần quyền laravel
Giao diện quản lý permission trong phân quyền laravel

resources/views/permisisons/index.blade.php

@extends('layouts.app')

@section('title', ' | List Permissions')

@section('content')
<div class="col-md-12">
    <div class="card">
        <div class="card-header">
            <h2 class="card-title">Permission Management</h2>
            <div class="card-tools">
                <button type="button" class="btn btn-success btn-create" data-toggle="modal" data-target="#PermissionModal">
                <i class="fas fa-plus-square"></i> Add Permission
                </button>
            </div>
            
        </div>
        <!-- /.card-header -->
        <div class="card-body table-responsive">  
            <table id="permission_table" class="table table-bordered data-table">
                <thead>
                    <tr class="bg-blue">                    
                        <th width="50px">No</th>
                        <th>Name</th>
                        <th width="150px">Action</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </div>
        <!-- /.card-body -->
    </div>
    <!-- /.card -->
</div>
<!-- Modal UpadateOrCreate Permission -->

<div class="modal fade" id="PermissionModal">
    <div class="modal-dialog modal-md">
        <div class="modal-content">
            <form method="POST" action="" id="permissionForm">        
            @csrf
            <div class="modal-header">
                <h4 class="modal-title">Add permission</h4>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                <input type="hidden" name="_method" id="permission_method" value="POST">
                <input type="hidden" name="id" id="id" value="">
                <div class="form-group">
                    <label for="name" class="col-sm-2 control-label">Name</label>
                    <div class="col-sm-12">
                        <input type="text" class="form-control @error('name') is-invalid @enderror" id="name" name="name" placeholder="Enter Name" value="" required>
                        @error('name')
                            <p class="mt-2 mb-0 error text-danger">{{ $message }}</p>
                        @enderror
                    </div>
                </div>
            </div>
            <div class="modal-footer justify-content-between">
                <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                <button type="submit" class="btn btn-primary" id="savedata">Save</button>
            </div>
            </form>
        </div>
        <!-- /.modal-content -->
    </div>
    <!-- /.modal-dialog -->
</div>
<!-- /.modal -->
@endsection

@section('scripts')

<script type="text/javascript">
  $(function () {

    var change = $('#permission_table').DataTable({
            'responsive': true,
            //'fixedHeader': true,
            'autoWidth': false,
            'processing': true,
            'serverside': true,
            "lengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "Show all"]],
            'ajax': {
                'dataSrc': 'permissions'
            },
            'columns':[   
                {
                    className:      'dt-control',
                    orderable:      false,
                    data:           null,
                    defaultContent: ''
                },
                { data: 'name' },
                { data: 'id',
                    orderable: false,
                    render: function(data){
                        return '<button class="btn btn-sm btn-info btn-edit mr-1" data-id="'+data+'">Edit</button>'+
                                '<button class="btn btn-sm btn-danger btn-delete" data-id="'+data+'">Delete</button>';
                    }
                }
            ],
            order: [[1, 'desc']],
            "columnDefs": [
				{ 'className': 'dt-center','targets': '_all' }
			]
        });
        
        //Plus detail    
        $('#permission_table tbody').on('click', 'td.dt-control', function () {
            var tr = $(this).closest('tr');
            var row = change.row( tr );
        
            if ( row.child.isShown() ) {
                row.child.hide();
                tr.removeClass('shown');
            }
            else {
                row.child( format(row.data()) ).show();
                tr.addClass('shown');
            }
        } );
        function format ( rowData ) {
            return '<table class="table table-bordered">'+
                '<tr style="background: #f9f9f9">'+
                    '<th width="30%">Title</th>'+
                    '<th width="70%">Details</th>'+
                '</tr>'+
                '<tr>'+
                    '<td>ID:</td>'+
                    '<td>'+rowData.id+'</td>'+
                '</tr>'+
                '<tr>'+
                    '<td>Name:</td>'+
                    '<td>'+rowData.name+'</td>'+
                '</tr>'+
                '<tr>'+
                    '<td>Created at:</td>'+
                    '<td>'+Date(rowData.created_at)+'</td>'+
                '</tr>'+
            '</table>'; 
        };

        //create
        $('#permission_table').on('click', '.btn-create', function (e) {
            e.preventDefault;
            var url = '{{ route("permissions.store") }}';
            $('.modal-title').html("Create permission");
            $('#permissionForm').attr('action', url);
            $('#permission_method').attr('value', 'POST');
            $('#id').val('');        
        });
        //edit
        $('#permission_table').on('click', '.btn-edit', function () {
            var permission_id = $(this).data('id');
            var url = '{{ route("permissions.update","") }}' +'/'+ permission_id;
            $.ajax({
                cache: false,
                success: function(data){
                    $('#PermissionModal').modal('show');
                    $('.modal-title').html("Edit permission");
                    $.each(data.permissions, function(index, value) {
                        if(value.id === permission_id){
                            $('#id').val(permission_id);
                            $('#name').val(value.name);
                            $('#permissionForm').attr('action', url);
                            $('#permission_method').attr('value', 'PATCH');
                         }
                    });
                }
            });
        });
        $('#PermissionModal').on('hidden.bs.modal', function () {
            $(this).find('form').trigger('reset');  
            $('.error').html('');
            $('#name').removeClass("is-invalid");
        });

        //Delete         
        $('#permission_table').on("click", ".btn-delete", function() { 
            var permission_id = $(this).data('id');
            var url = '{{ route("permissions.destroy","") }}' +'/'+ permission_id;
            Swal.fire({
                title: 'Are you sure you want to delete this record?',
                text: "If you delete this, it will be gone forever.",
                icon: 'warning',
                confirmButtonColor: '#3085d6',
                cancelButtonColor: '#d33',
                confirmButtonText: 'Yes, delete it!',
                showDenyButton: true,
                denyButtonText: 'Cancel',
            }).then((result) => {
                if (result.isConfirmed) {
                    $.ajax({
                        url: url,
                        type: 'DELETE',
                        cache: false,
                        data: {
                            _token:'{{ csrf_token() }}',
                        },
                        success: function (response){
                            Swal.fire(
                                "Deleted!", 
                                "Your file has been deleted.", 
                                "success"
                                ).then(function(){ 
                                    location.reload();
                                });                            
                        }
                    });
                }else if (result.isDenied) {
                    Swal.fire('Your record is safe', '', 'info')
                }         
                
            });            
        });

        @if(count($errors))
            $('#PermissionModal').modal('show');
        @endif        

});
</script>
@endsection

Bước 8: tạo seeder để thêm dữ liệu vào bảng

Bạn gõ lần lượt 2 dòng lệnh dưới đây để tạo seeder có tên PermissionTableSeeder.php và CreateAdminUserSeeder.php

php artisan make:seeder PermissionTableSeeder

//và sau đó

php artisan make:seeder CreateAdminUserSeeder

Và tiếp theo là lần lượt thêm vào nội dung vào 2 file đó.

database/seeds/PermissionTableSeeder.php

<?php
  
namespace Database\Seeders;
  
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
  
class PermissionTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $permissions = [
            'user-menu',
            'user-list',
            'user-create',
            'user-edit',
            'user-delete',

            'role-menu',
            'role-list',
            'role-create',
            'role-edit',
            'role-delete',

            'permission-menu',
            'permission-list',
            'permission-create',
            'permission-edit',
            'permission-delete',

        ];
     
        foreach ($permissions as $permission) {
             Permission::create(['name' => $permission]);
        }
    }
}

database/seeds/CreateAdminUserSeeder.php

<?php
  
namespace Database\Seeders;
  
use Illuminate\Database\Seeder;
use App\Models\User;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
  
class CreateAdminUserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $user = User::create([
            'name' => 'Super Admin', 
            'email' => 'superadmin@gmail.com',
            'password' => bcrypt('123456')
        ]);
    
        $role = Role::create(['name' => 'Super-Admin']);
    
        $permissions = Permission::pluck('id','id')->all();
   
        $role->syncPermissions($permissions);
     
        $user->assignRole([$role->id]);
    }
}

Sau khi đã làm đầy đủ cho 2 file seeder trên thì chúng ta chạy lệnh trong command lần lượt 2 câu lệnh bên dưới.

php artisan db:seed --class=PermissionTableSeeder

php artisan db:seed --class=CreateAdminUserSeeder

Thiết lập quyền Super Admin trong phần quyền laravel

Trong phân quyền thì hầu hết đều có role Super Admin, role này dùng cho người quản trị có quyền cao nhất và có thể xử lý tất cả các tác vụ trong hệ thống của bạn. Và đặt biệt chỉ có role này mới có quyền tạo ra permissions list.

Để quyền Super Admin có thể chạy thì chúng ta vào trong file AuthServiceProvider.php và thêm vào đoạn code bên dưới.

app/Providers/AuthServiceProvider.php

public function boot()
    {
        $this->registerPolicies();

        // Implicitly grant "Super-Admin" role all permission checks using can()
        Gate::before(function ($user, $ability) {
            return $user->hasRole('Super-Admin') ? true : null;
        });

        /* define a admin user role */
        Gate::define('isSuperAdmin', function($user) {
            return $user->role == 'Super-Admin';
         });
    }

Kết luận

Trên đây là hướng dẫn chi tiết nhất về cách phân quyền trong laravel với sự hỗ trợ của spatie laravel permission. Mặc dù phân quyền trong laravel nhìn đơn giản vậy nhưng khá nhức đầu và phức tạp đối với những người tự học laravel.

Và để hướng dẫn phân quyền trên hoàn hảo hơn thì bạn có thể chỉnh sửa và chế biến nó một chút theo ý của riêng mình.

Có 2 bình luận
  1. dạ chào anh chị ạ, em có tham khảo và làm giống bài trên nhưng khi vào index users thì lại k có quyền vào ạ(USER DOES NOT HAVE THE RIGHT PERMISSIONS.) Anh/chị giải đáp giúp e với ạ

    1. Nếu bạn đang vào bằng Super Admin thì thử kiểm tra lại phần thiết lập super admin.
      Các user còn lại thì bạn xem đoạn function __construct()
      {
      $this->middleware(‘permission:user-list|user-create|user-edit|user-delete’, [‘only’ => [‘index’,’store’]]);
      $this->middleware(‘permission:user-create’, [‘only’ => [‘create’,’store’]]);
      $this->middleware(‘permission:user-edit’, [‘only’ => [‘edit’,’update’]]);
      $this->middleware(‘permission:user-delete’, [‘only’ => [‘destroy’]]);
      }
      đã đúng chưa nhé.

Để lại bình luận

Email của bạn sẽ được bảo mật.