(Part 5) Membuat Aplikasi POS (Point of Sales) Laravel 5.6 - Role & Permission Users

(Part 5) Membuat Aplikasi POS (Point of Sales) Laravel 5.6 - Role & Permission Users

Pendahuluan

Dalam proses penulisan ini, banyak tambahan informasi yang masuk sehingga saya memutuskan untuk menambahkan fitur dari aplikasi yang kita buat. Salah satunya yakni, nantinya aplikasi ini akan memiliki beberapa user yang mempunyai peran masing-masing. Misalnya saja, user untuk mengelola data product saja, user yang bertindak sebagai admin, user yang bertindak untuk melakukan transaksi dan lain sebagainya.

Dengan memanfaatkan fitur role & permission, maka user tidak dapat melakukan tindakan diluar dari role / permission yang dimilikinya.

Baca Juga: Membuat Aplikasi Point of Sales (POS) - Otentikasi User

Install Library Laravel-permission

Untuk mengatur role & permission kedepannya, kita akan memanfaatkan salah satu package yang cukup banyak digunakan sehingga kita tidak perlu membuatnya dari awal lagi, karena fitur yang disediakan cukup lengkap dan sesuai dengan kebutuhan kita. Package ini dibuat oleh Spatie, dengan nama repo laravel-permission.

Install package ini terlebih dahulu dengan composer:

composer require spatie/laravel-permission

Kemudian publish migration-nya:

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

Lakukan migrate:

php artisan migrate

Maka kita akan mendapatkan 5 tambahan table baru.

role & permission laravel - table

Lakukan konfigurasi agar package ini dapat digunakan pada model User, maka tambahkan use statement pada model User.php:

use Spatie\Permission\Traits\HasRoles;
​
//di dalam class
use Notifiable, HasRoles;

Baca Juga: Membuat Chart Google Analystic Server Side Authorization Dengan PHP

Manajemen Role

Kita akan membuat CRUD untuk role, sehingga dapat melakukan manipulasi data role dari aplikasi secara langsung. Sebenarnya package ini menyediakan command untuk membuat role atau permission:

php artisan permission:create-role writer
php artisan permission:create-permission "edit articles"
php artisan permission:create-role writer web
php artisan permission:create-permission "edit articles" web

Akan tetapi dalam tulisan ini, saya akan skip command tersebut sehingga tidak akan kita gunakan. Namun apabila kamu malas untuk membuat crud role-nya, kamu bisa memilih opsi diatas agar lebih cepat dan dapat melangkan ke step selanjutnya.

Baik, kita akan lanjut untuk membuat fitur manajemen role-nya, buat controller dengan command:

php artisan make:controller RoleController

Kemudian tambahkan method berikut kedalam RoleController.php:

public function index()
{
    $role = Role::orderBy('created_at', 'DESC')->paginate(10);
    return view('role.index', compact('role'));
}

Sedangkan untuk memanggil modelnya buka dari App, melainkan dari Spatie/Permission/Models, tambahkan use statement berikut:

use Spatie\Permission\Models\Role;

Kemudian buat file index.blade.php di dalam folder resources/views/role (Note: apabil folder role tidak ditemukan, silahkan dibuat). Masukkan code berikut:

@extends('layouts.master')
​
@section('title')
    <title>Manajemen Role</title>
@endsection
​
@section('content')
    <div class="content-wrapper">
        <div class="content-header">
            <div class="container-fluid">
                <div class="row mb-2">
                    <div class="col-sm-6">
                        <h1 class="m-0 text-dark">Manajemen Role</h1>
                    </div>
                    <div class="col-sm-6">
                        <ol class="breadcrumb float-sm-right">
                            <li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
                            <li class="breadcrumb-item active">Role</li>
                        </ol>
                    </div>
                </div>
            </div>
        </div>
​
        <section class="content">
            <div class="container-fluid">
                <div class="row">
                    <div class="col-md-4">
                        @card
                            @slot('title')
                            Tambah
                            @endslot
                            
                            @if (session('error'))
                                @alert(['type' => 'danger'])
                                    {!! session('error') !!}
                                @endalert
                            @endif
​
                            <form role="form" action="{{ route('role.store') }}" method="POST">
                                @csrf
                                <div class="form-group">
                                    <label for="name">Role</label>
                                    <input type="text" 
                                    name="name"
                                    class="form-control {{ $errors->has('name') ? 'is-invalid':'' }}" id="name" required>
                                </div>
                            @slot('footer')
                                <div class="card-footer">
                                    <button class="btn btn-primary">Simpan</button>
                                </div>
                            </form>
                            @endslot
                        @endcard
                    </div>
                    <div class="col-md-8">
                        @card
                            @slot('title')
                            List Role
                            @endslot
                            
                            @if (session('success'))
                                @alert(['type' => 'success'])
                                    {!! session('success') !!}
                                @endalert
                            @endif
                            
                            <div class="table-responsive">
                                <table class="table table-hover">
                                    <thead>
                                        <tr>
                                            <td>#</td>
                                            <td>Role</td>
                                            <td>Guard</td>
                                            <td>Created At</td>
                                            <td>Aksi</td>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        @php $no = 1; @endphp
                                        @forelse ($role as $row)
                                        <tr>
                                            <td>{{ $no++ }}</td>
                                            <td>{{ $row->name }}</td>
                                            <td>{{ $row->guard_name }}</td>
                                            <td>{{ $row->created_at }}</td>
                                            <td>
                                                <form action="{{ route('role.destroy', $row->id) }}" method="POST">
                                                    @csrf
                                                    <input type="hidden" name="_method" value="DELETE">
                                                    <button class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></button>
                                                </form>
                                            </td>
                                        </tr>
                                        @empty
                                        <tr>
                                            <td colspan="5" class="text-center">Tidak ada data</td>
                                        </tr>
                                        @endforelse
                                    </tbody>
                                </table>
                            </div>
​
                            <div class="float-right">
                                {!! $role->links() !!}
                            </div>
                            @slot('footer')
​
                            @endslot
                        @endcard
                    </div>
                </div>
            </div>
        </section>
    </div>
@endsection

Buka file resources/views/layouts/module/sidebar.blade.php, kemudian tambahkan code berikut:

<li class="nav-item has-treeview">
    <a href="#" class="nav-link">
        <i class="nav-icon fa fa-users"></i>
        <p>
            Manajemen Users
            <i class="right fa fa-angle-left"></i>
        </p>
    </a>
    <ul class="nav nav-treeview">
        <li class="nav-item">
            <a href="{{ route('role.index') }}" class="nav-link">
                <i class="fa fa-circle-o nav-icon"></i>
                <p>Role</p>
            </a>
        </li>
    </ul>
</li>

Lalu buat file routes/web.php, tambahkan route untuk role:

Route::resource('/role', 'RoleController')->except([
    'create', 'show', 'edit', 'update'
]);

Saya tidak memberikan penjelasan setelah code sebagaimana yang biasa saya lakukan, karena hal ini sudah kita lalui berulang kali, hanya berbeda kondisi saja. Dengan tidak menambahkan penjelasan lagi pada bagian ini, saya berharap kamu memahami materi sebelumnya sehingga tidak hanya copy paste atau mengikutinya saja tanpa ada yang tertinggal untuk kamu pahami.

Oke, lanjut! Buka kembali RoleController.php kemudian tambahkan method:

public function store(Request $request)
{
    $this->validate($request, [
        'name' => 'required|string|max:50'
    ]);
​
    $role = Role::firstOrCreate(['name' => $request->name]);
    return redirect()->back()->with(['success' => 'Role: <strong>' . $role->name . '</strong> Ditambahkan']);
}

Note: Menggunakan firstOrCreate agar tidak terjadi duplicate role dengan name yang sama. Sedangkan untuk field guard_name jika tidak di-isi maka secara otomatis akan menggunakan guard default dari Laravel, yakni web. Kebutuhan kita saat ini masih menggunakan guard default dari Laravel.

Untuk menghapus role, tambahkan method:

public function destroy($id)
{
    $role = Role::findOrFail($id);
    $role->delete();
    return redirect()->back()->with(['success' => 'Role: <strong>' . $role->name . '</strong> Dihapus']);
}

Manajemen Users

Mengatur role / permission user berarti menghubungkan antara user dan role atau permission yang akan dimilikinya, maka sebelum melakukan itu, hal yang harus dilakukan adalah membuat fitur manajemen user . Masih berkaitan dengan CRUD, maka sepanjang sub topic kali ini, saya tidak akan memberikan penjelasan setelah baris code agar artikel ini tidak terlalu panjang.

Buat controller terlebih dahulu dengan command:

php artisan make:controller UserController

Agar lebih cepat, saya akan membuat semua method sekaligus karena tidak ada yang perlu dijelaskan lagi dari tiap baris code-nya. Buka file UserController.php, kemudian tambahkan code berikut:

<?php
​
namespace App\Http\Controllers;
​
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;
use App\User;
​
class UserController extends Controller
{
    public function index()
    {
        $users = User::orderBy('created_at', 'DESC')->paginate(10);
        return view('users.index', compact('users'));
    }
​
    public function create()
    {
        $role = Role::orderBy('name', 'ASC')->get();
        return view('users.create', compact('role'));
    }
​
    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required|string|max:100',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:6',
            'role' => 'required|string|exists:roles,name'
        ]);
​
        $user = User::firstOrCreate([
            'email' => $request->email
        ], [
            'name' => $request->name,
            'password' => bcrypt($request->password),
            'status' => true
        ]);
​
        $user->assignRole($request->role);
        return redirect(route('users.index'))->with(['success' => 'User: <strong>' . $user->name . '</strong> Ditambahkan']);
    }
​
    public function edit($id)
    {
        $user = User::findOrFail($id);
        return view('users.edit', compact('user'));
    }
​
    public function update(Request $request, $id)
    {
        $this->validate($request, [
            'name' => 'required|string|max:100',
            'email' => 'required|email|exists:users,email',
            'password' => 'nullable|min:6',
        ]);
​
        $user = User::findOrFail($id);
        $password = !empty($request->password) ? bcrypt($request->password):$user->password;
        $user->update([
            'name' => $request->name,
            'password' => $password
        ]);
        return redirect(route('users.index'))->with(['success' => 'User: <strong>' . $user->name . '</strong> Diperbaharui']);
    }
​
    public function destroy($id)
    {
        $user = User::findOrFail($id);
        $user->delete();
        return redirect()->back()->with(['success' => 'User: <strong>' . $user->name . '</strong> Dihapus']);
    }
}

Buka file resources/views/users/index.blade.php untuk view menampilkan data user, kemudian masukkan potongan code berikut:

@extends('layouts.master')
​
@section('title')
    <title>Manajemen User</title>
@endsection
​
@section('content')
    <div class="content-wrapper">
        <div class="content-header">
            <div class="container-fluid">
                <div class="row mb-2">
                    <div class="col-sm-6">
                        <h1 class="m-0 text-dark">Manajemen User</h1>
                    </div>
                    <div class="col-sm-6">
                        <ol class="breadcrumb float-sm-right">
                            <li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
                            <li class="breadcrumb-item active">User</li>
                        </ol>
                    </div>
                </div>
            </div>
        </div>
​
        <section class="content">
            <div class="container-fluid">
                <div class="row">
                    <div class="col-md-12">
                        @card
                            @slot('title')
                            <a href="{{ route('users.create') }}" class="btn btn-primary btn-sm">Tambah Baru</a>
                            @endslot
                            
                            @if (session('success'))
                                @alert(['type' => 'success'])
                                    {!! session('success') !!}
                                @endalert
                            @endif
                            
                            <div class="table-responsive">
                                <table class="table table-hover">
                                    <thead>
                                        <tr>
                                            <td>#</td>
                                            <td>Nama</td>
                                            <td>Email</td>
                                            <td>Role</td>
                                            <td>Status</td>
                                            <td>Aksi</td>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        @php $no = 1; @endphp
                                        @forelse ($users as $row)
                                        <tr>
                                            <td>{{ $no++ }}</td>
                                            <td>{{ $row->name }}</td>
                                            <td>{{ $row->email }}</td>
                                            <td>
                                                @foreach ($row->getRoleNames() as $role)
                                                <label for="" class="badge badge-info">{{ $role }}</label>
                                                @endforeach
                                            </td>
                                            <td>
                                                @if ($row->status)
                                                <label class="badge badge-success">Aktif</label>
                                                @else
                                                <label for="" class="badge badge-default">Suspend</label>
                                                @endif
                                            </td>
                                            <td>
                                                <form action="{{ route('users.destroy', $row->id) }}" method="POST">
                                                    @csrf
                                                    <input type="hidden" name="_method" value="DELETE">
                                                    <a href="{{ route('users.roles', $row->id) }}" class="btn btn-info btn-sm"><i class="fa fa-user-secret"></i></a>
                                                    <a href="{{ route('users.edit', $row->id) }}" class="btn btn-warning btn-sm"><i class="fa fa-edit"></i></a>
                                                    <button class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></button>
                                                </form>
                                            </td>
                                        </tr>
                                        @empty
                                        <tr>
                                            <td colspan="4" class="text-center">Tidak ada data</td>
                                        </tr>
                                        @endforelse
                                    </tbody>
                                </table>
                            </div>
                            @slot('footer')
​
                            @endslot
                        @endcard
                    </div>
                </div>
            </div>
        </section>
    </div>
@endsection

Selanjutnya, buat file resources/views/users/create.blade.php untuk menambahkan user:

@extends('layouts.master')
​
@section('title')
    <title>Add New Users</title>
@endsection
​
@section('content')
    <div class="content-wrapper">
        <div class="content-header">
            <div class="container-fluid">
                <div class="row mb-2">
                    <div class="col-sm-6">
                        <h1 class="m-0 text-dark">Add New Users</h1>
                    </div>
                    <div class="col-sm-6">
                        <ol class="breadcrumb float-sm-right">
                            <li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
                            <li class="breadcrumb-item"><a href="{{ route('users.index') }}">User</a></li>
                            <li class="breadcrumb-item active">Add New</li>
                        </ol>
                    </div>
                </div>
            </div>
        </div>
​
        <section class="content">
            <div class="container-fluid">
                <div class="row">
                    <div class="col-md-12">
                        @card
                            @slot('title')
                            
                            @endslot
                            
                            @if (session('error'))
                                @alert(['type' => 'danger'])
                                    {!! session('error') !!}
                                @endalert
                            @endif
                            
                            <form action="{{ route('users.store') }}" method="post">
                                @csrf
                                <div class="form-group">
                                    <label for="">Nama</label>
                                    <input type="text" name="name" class="form-control {{ $errors->has('name') ? 'is-invalid':'' }}" required>
                                    <p class="text-danger">{{ $errors->first('name') }}</p>
                                </div>
                                <div class="form-group">
                                    <label for="">Email</label>
                                    <input type="email" name="email" class="form-control {{ $errors->has('email') ? 'is-invalid':'' }}" required>
                                    <p class="text-danger">{{ $errors->first('email') }}</p>
                                </div>
                                <div class="form-group">
                                    <label for="">Password</label>
                                    <input type="password" name="password" class="form-control {{ $errors->has('password') ? 'is-invalid':'' }}" required>
                                    <p class="text-danger">{{ $errors->first('password') }}</p>
                                </div>
                                <div class="form-group">
                                    <label for="">Role</label>
                                    <select name="role" class="form-control {{ $errors->has('role') ? 'is-invalid':'' }}" required>
                                        <option value="">Pilih</option>
                                        @foreach ($role as $row)
                                        <option value="{{ $row->name }}">{{ $row->name }}</option>
                                        @endforeach
                                    </select>
                                    <p class="text-danger">{{ $errors->first('role') }}</p>
                                </div>
                                <div class="form-group">
                                    <button class="btn btn-primary btn-sm">
                                        <i class="fa fa-send"></i> Simpan
                                    </button>
                                </div>
                            </form>
                            @slot('footer')
​
                            @endslot
                        @endcard
                    </div>
                </div>
            </div>
        </section>
    </div>
@endsection

Terakhir, buat file resources/views/users/edit.blade.php untuk edit data:

@extends('layouts.master')
​
@section('title')
    <title>Edit Users</title>
@endsection
​
@section('content')
    <div class="content-wrapper">
        <div class="content-header">
            <div class="container-fluid">
                <div class="row mb-2">
                    <div class="col-sm-6">
                        <h1 class="m-0 text-dark">Edit Users</h1>
                    </div>
                    <div class="col-sm-6">
                        <ol class="breadcrumb float-sm-right">
                            <li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
                            <li class="breadcrumb-item"><a href="{{ route('users.index') }}">User</a></li>
                            <li class="breadcrumb-item active">Edit</li>
                        </ol>
                    </div>
                </div>
            </div>
        </div>
​
        <section class="content">
            <div class="container-fluid">
                <div class="row">
                    <div class="col-md-12">
                        @card
                            @slot('title')
                            
                            @endslot
                            
                            @if (session('error'))
                                @alert(['type' => 'danger'])
                                    {!! session('error') !!}
                                @endalert
                            @endif
                            
                            <form action="{{ route('users.update', $user->id) }}" method="post">
                                @csrf
                                <input type="hidden" name="_method" value="PUT">
                                <div class="form-group">
                                    <label for="">Nama</label>
                                    <input type="text" name="name" 
                                        value="{{ $user->name }}"
                                        class="form-control {{ $errors->has('name') ? 'is-invalid':'' }}" required>
                                    <p class="text-danger">{{ $errors->first('name') }}</p>
                                </div>
                                <div class="form-group">
                                    <label for="">Email</label>
                                    <input type="email" name="email" 
                                        value="{{ $user->email }}"
                                        class="form-control {{ $errors->has('email') ? 'is-invalid':'' }}" 
                                        required readonly>
                                    <p class="text-danger">{{ $errors->first('email') }}</p>
                                </div>
                                <div class="form-group">
                                    <label for="">Password</label>
                                    <input type="password" name="password" 
                                        class="form-control {{ $errors->has('password') ? 'is-invalid':'' }}">
                                    <p class="text-danger">{{ $errors->first('password') }}</p>
                                    <p class="text-warning">Biarkan kosong, jika tidak ingin mengganti password</p>
                                </div>
                                <div class="form-group">
                                    <button class="btn btn-primary btn-sm">
                                        <i class="fa fa-send"></i> Update
                                    </button>
                                </div>
                            </form>
                            @slot('footer')
​
                            @endslot
                        @endcard
                    </div>
                </div>
            </div>
        </section>
    </div>
@endsection

Jangan lupa untuk menambahkan route-nya, buka file routes/web.php kemudian tambahkan:

Route::resource('/users', 'UserController')->except([
    'show'
]);
Route::get('/users/roles/{id}', 'UserController@roles')->name('users.roles');

Agar bisa di-klik dari sidebar menu, buka file resources/views/layouts/module/sidebar.blade.php, kemudian tambahkan:

<li class="nav-item">
    <a href="{{ route('users.index') }}" class="nav-link">
        <i class="fa fa-circle-o nav-icon"></i>
        <p>Users</p>
    </a>
</li>

Baca Juga: Menggunakan Component di Vue.js

Set Permissions Each Role

Schema yang diinginkan adalah dimana masing-masing role memiliki permission sesuai dengan level user-nya. Misal, untuk role admin memiliki permission untuk mengakses semua fitur yang dimiliki oleh aplikasi tersebut. Sedangkan role operator hanya memiliki akses untuk mengatur data products, serta role kasir hanya memiliki permission untuk melakukan transaksi.

Kita sudah memiliki fitur manajemen role, maka selanjutnya yang akan dibuat adalah fitur untuk menambahkan apa saja permission dari aplikasi ini serta sekaligus juga mengeset permission untuk masing-masing role. Masih dengan UserController.php, tambahkan method:

public function rolePermission(Request $request)
{
    $role = $request->get('role');
    
    //Default, set dua buah variable dengan nilai null
    $permissions = null;
    $hasPermission = null;
    
    //Mengambil data role
    $roles = Role::all()->pluck('name');
    
    //apabila parameter role terpenuhi
    if (!empty($role)) {
        //select role berdasarkan namenya, ini sejenis dengan method find()
        $getRole = Role::findByName($role);
        
        //Query untuk mengambil permission yang telah dimiliki oleh role terkait
        $hasPermission = DB::table('role_has_permissions')
            ->select('permissions.name')
            ->join('permissions', 'role_has_permissions.permission_id', '=', 'permissions.id')
            ->where('role_id', $getRole->id)->get()->pluck('name')->all();
        
        //Mengambil data permission
        $permissions = Permission::all()->pluck('name');
    }
    return view('users.role_permission', compact('roles', 'permissions', 'hasPermission'));
}

Penjelasan: Jadi, list permission yang dimiliki oleh role terkait baru akan ditampilkan setelah parameter role terpenuhi, kita mengambilnya dengan method GET.

Jangan lupa untuk menambahkan use statement DB dan model Permission;

use Spatie\Permission\Models\Permission;
use DB;

Buat view dengan nama role_permission.blade.php di dalam folder users:

@extends('layouts.master')
​
@section('title')
    <title>Role Permission</title>
@endsection
​
@section('css')
    <style type="text/css">
        .tab-pane{
            height:150px;
            overflow-y:scroll;
        }
    </style>
@endsection
​
@section('content')
    <div class="content-wrapper">
        <div class="content-header">
            <div class="container-fluid">
                <div class="row mb-2">
                    <div class="col-sm-6">
                        <h1 class="m-0 text-dark">Role Permission</h1>
                    </div>
                    <div class="col-sm-6">
                        <ol class="breadcrumb float-sm-right">
                            <li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
                            <li class="breadcrumb-item active">Role Permission</li>
                        </ol>
                    </div>
                </div>
            </div>
        </div>
​
        <section class="content">
            <div class="container-fluid">
                <div class="row">
                    <div class="col-md-4">
                        @card
                            @slot('title')
                            <h4 class="card-title">Add New Permission</h4>
                            @endslot
​
                            <form action="{{ route('users.add_permission') }}" method="post">
                                @csrf
                                <div class="form-group">
                                    <label for="">Name</label>
                                    <input type="text" name="name" class="form-control {{ $errors->has('name') ? 'is-invalid':'' }}" required>
                                    <p class="text-danger">{{ $errors->first('name') }}</p>
                                </div>
                                <div class="form-group">
                                    <button class="btn btn-primary btn-sm">
                                        Add New
                                    </button>
                                </div>
                            </form>
                            @slot('footer')
​
                            @endslot
                        @endcard
                    </div>
​
                    <div class="col-md-8">
                        @card
                            @slot('title')
                            Set Permission to Role
                            @endslot
​
                            @if (session('success'))
                                @alert(['type' => 'success'])
                                    {{ session('success') }}
                                @endalert
                            @endif
                            
                            <form action="{{ route('users.roles_permission') }}" method="GET">
                                <div class="form-group">
                                    <label for="">Roles</label>
                                    <div class="input-group">
                                        <select name="role" class="form-control">
                                            @foreach ($roles as $value)
                                                <option value="{{ $value }}" {{ request()->get('role') == $value ? 'selected':'' }}>{{ $value }}</option>
                                            @endforeach
                                        </select>
                                        <span class="input-group-btn">
                                            <button class="btn btn-danger">Check!</button>
                                        </span>
                                    </div>
                                </div>
                            </form>
                            
                            {{-- jika $permission tidak bernilai kosong --}}
                            @if (!empty($permissions))
                                <form action="{{ route('users.setRolePermission', request()->get('role')) }}" method="post">
                                    @csrf
                                    <input type="hidden" name="_method" value="PUT">
                                    <div class="form-group">
                                        <div class="nav-tabs-custom">
                                            <ul class="nav nav-tabs">
                                                <li class="active">
                                                    <a href="#tab_1" data-toggle="tab">Permissions</a>
                                                </li>
                                            </ul>
                                            <div class="tab-content">
                                                <div class="tab-pane active" id="tab_1">
                                                    @php $no = 1; @endphp
                                                    @foreach ($permissions as $key => $row)
                                                        <input type="checkbox" 
                                                            name="permission[]" 
                                                            class="minimal-red" 
                                                            value="{{ $row }}"
                                                            {{--  CHECK, JIKA PERMISSION TERSEBUT SUDAH DI SET, MAKA CHECKED --}}
                                                            {{ in_array($row, $hasPermission) ? 'checked':'' }}
                                                            > {{ $row }} <br>
                                                        @if ($no++%4 == 0)
                                                        <br>
                                                        @endif
                                                    @endforeach
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                    
                                    <div class="pull-right">
                                        <button class="btn btn-primary btn-sm">
                                            <i class="fa fa-send"></i> Set Permission
                                        </button>
                                    </div>
                                </form>
                            @endif
                            @slot('footer')
                               
                            @endslot
                        @endcard
                    </div>
                </div>
            </div>
        </section>
    </div>
@endsection

Penjelasan: Grid pertama adalah form untuk menambahkan data permission. Grid kedua untuk menampilkan list role dan juga list permission dimana nantinya akan ter-check apabila permission untuk role terkait telah di-set.

role & permission laravel - pos

Mari kita selesaikan route pertama untuk meng-handle permintaanya form menambahkan data permission. Buka UserController.php kemudian tambahkan method:

public function addPermission(Request $request)
{
    $this->validate($request, [
        'name' => 'required|string|unique:permissions'
    ]);
​
    $permission = Permission::firstOrCreate([
        'name' => $request->name
    ]);
    return redirect()->back();
}

Note: Hanya proses insert data seperti biasa.

Buka file routes/web.php kemudian tambahkan:

Route::post('/users/permission', 'UserController@addPermission')->name('users.add_permission');
Route::get('/users/role-permission', 'UserController@rolePermission')->name('users.roles_permission');

Route selanjutnya yang akan kita selesaikan bertugas untuk meng-handle permission yang dipilih untuk kemudian di-assign ke role yang diinginkan. Buka file UserController.php kemudian tambahkan method:

public function setRolePermission(Request $request, $role)
{
    //select role berdasarkan namanya
    $role = Role::findByName($role);
    
    //fungsi syncPermission akan menghapus semua permissio yg dimiliki role tersebut
    //kemudian di-assign kembali sehingga tidak terjadi duplicate data
    $role->syncPermissions($request->permission);
    return redirect()->back()->with(['success' => 'Permission to Role Saved!']);
}

Penjelasan: Perhatikan code sebelumnya <form action="{{ route('users.setRolePermission', request()->get('role')) }}" method="post">, kita mengirim nama dari role yang pilih sebagai parameter yang nantinya digunakan untuk diberikan permission.

Selanjutnya, buka routes/web.php kemudian tambahkan:

Route::put('/users/permission/{role}', 'UserController@setRolePermission')->name('users.setRolePermission');

role & permission laravel - pos

Ohya, jangan lupa tambahkan pada sidebar.blade.php agar bisa diklik dari sidebar menu. Sematkan code berikut di dalam dropdown Manajemen Users:

<li class="nav-item">
    <a href="{{ route('users.roles_permission') }}" class="nav-link">
        <i class="fa fa-circle-o nav-icon"></i>
        <p>Role Permission</p>
    </a>
</li>

Change Permission User

Pada sub topic manajemen users, kita memiliki tiga button, yakni: hapus, edit dan yang terakhir untuk mengubah role yang dimiliki oleh users.

role & permission laravel - pos

Akan tetap tombol tersebut masih belum berfungsi sebab hanya route-nya saja yang sudah didefinisikan. Buka kembali file UserController.php kemudian tambahkan method:

public function roles(Request $request, $id)
{
    $user = User::findOrFail($id);
    $roles = Role::all()->pluck('name');
    return view('users.roles', compact('user', 'roles'));
}

Buat file untuk view-nya dengan roles.blade.php di dalam folder resources/views/users:

@extends('layouts.master')
​
@section('title')
    <title>Set Role</title>
@endsection
​
@section('content')
    <div class="content-wrapper">
        <div class="content-header">
            <div class="container-fluid">
                <div class="row mb-2">
                    <div class="col-sm-6">
                        <h1 class="m-0 text-dark">Set Role</h1>
                    </div>
                    <div class="col-sm-6">
                        <ol class="breadcrumb float-sm-right">
                            <li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
                            <li class="breadcrumb-item"><a href="{{ route('users.index') }}">User</a></li>
                            <li class="breadcrumb-item active">Set Role</li>
                        </ol>
                    </div>
                </div>
            </div>
        </div>
​
        <section class="content">
            <div class="container-fluid">
                <div class="row">
                    <div class="col-md-6">
                        <form action="{{ route('users.set_role', $user->id) }}" method="post">
                            @csrf
                            <input type="hidden" name="_method" value="PUT">
                        @card
                            @slot('title')
                            @endslot
                            
                            @if (session('success'))
                                @alert(['type' => 'success'])
                                    {{ session('success') }}
                                @endalert
                            @endif
                            
                            <div class="table-responsive">
                                <table class="table table-hover table-bordered">
                                    <thead>
                                        <tr>
                                            <th>Nama</th>
                                            <td>:</td>
                                            <td>{{ $user->name }}</td>
                                        </tr>
                                        <tr>
                                            <th>Email</th>
                                            <td>:</td>
                                            <td><a href="mailto:{{ $user->email }}">{{ $user->email }}</a></td>
                                        </tr>
                                        <tr>
                                            <th>Role</th>
                                            <td>:</td>
                                            <td>
                                                @foreach ($roles as $row)
                                                <input type="radio" name="role" 
                                                    {{ $user->hasRole($row) ? 'checked':'' }}
                                                    value="{{ $row }}"> {{  $row }} <br>
                                                @endforeach
                                            </td>
                                        </tr>
                                    </thead>
                                </table>
                            </div>
                            @slot('footer')
                                <div class="card-footer">
                                    <button type="submit" class="btn btn-primary btn-sm float-right">
                                        Set Role
                                    </button>
                                </div>
                            @endslot
                        @endcard
                        </form>
                    </div>
                </div>
            </div>
        </section>
    </div>
@endsection

Terakhir, tambahkan method berikut pada UserController.php untuk meng-handle perubahan role user:

public function setRole(Request $request, $id)
{
    $this->validate($request, [
        'role' => 'required'
    ]);
​
    $user = User::findOrFail($id);
    //menggunakan syncRoles agar terlebih dahulu menghapus semua role yang dimiliki
    //kemudian di-set kembali agar tidak terjadi duplicate
    $user->syncRoles($request->role);
    return redirect()->back()->with(['success' => 'Role Sudah Di Set']);
}

Middleware Permission

Mengatur role & permission adalah tentang mengatur wewenang dari setiap tingkatan user. Sehingga untuk menerapkan apa yang telah dibuat sebelumnya, maka kita dapat menggunakan middleware pada route untuk mengecek role & permission yang dimiliki oleh user yang sedang login.

Buka file app/Http/Kernel.php, kemudian tambahkan:

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

Mari kita modifikasi route, buka routes/web.php kemudian ubah menjadi:

<?php
​
Route::get('/', function() {
    return redirect(route('login'));
});
Auth::routes();
Route::group(['middleware' => 'auth'], function() {
    
    //Route yang berada dalam group ini hanya dapat diakses oleh user
    //yang memiliki role admin
    Route::group(['middleware' => ['role:admin']], function () {
        Route::resource('/role', 'RoleController')->except([
            'create', 'show', 'edit', 'update'
        ]);
​
        Route::resource('/users', 'UserController')->except([
            'show'
        ]);
        Route::get('/users/roles/{id}', 'UserController@roles')->name('users.roles');
        Route::put('/users/roles/{id}', 'UserController@setRole')->name('users.set_role');
        Route::post('/users/permission', 'UserController@addPermission')->name('users.add_permission');
        Route::get('/users/role-permission', 'UserController@rolePermission')->name('users.roles_permission');
        Route::put('/users/permission/{role}', 'UserController@setRolePermission')->name('users.setRolePermission');
    });
    
    
    //route yang berada dalam group ini, hanya bisa diakses oleh user
    //yang memiliki permission yang telah disebutkan dibawah
    Route::group(['middleware' => ['permission:show products|create products|delete products']], function() {
        Route::resource('/kategori', 'CategoryController')->except([
            'create', 'show'
        ]);
        Route::resource('/produk', 'ProductController');
    });
    
    //route group untuk kasir
    Route::group(['middleware' => ['role:kasir']], function() {
        
    });
    
    //home kita taruh diluar group karena semua jenis user yg login bisa mengaksesnya
    Route::get('/home', 'HomeController@index')->name('home');
});

Untuk menge-test-nya, tambahkan user baru dengan role selain admin, kemudian akses menu Manajemen product misalnya (Ingat: Role tersebut jangan di-set dulu permission-nya). Maka warning yang akan didapatkan akan tampak seperti ini

role & permission laravel - pos

Sidebar Menu Each User

Jika kita lihat, sidebar menu masih tampak seperti biasa. Tidak ada perbedaan menu yang ditampilkan untuk setiap level user

role & permission laravel - pos

Untuk mengecek role user, kita dapat menggunakan directive @role, buka file sidebar.blade.php kemudian modifikasi menjadi:

...
​
@role('admin')
<li class="nav-item has-treeview">
    <a href="#" class="nav-link">
        <i class="nav-icon fa fa-users"></i>
        <p>
            Manajemen Users
            <i class="right fa fa-angle-left"></i>
        </p>
    </a>
    <ul class="nav nav-treeview">
        <li class="nav-item">
            <a href="{{ route('role.index') }}" class="nav-link">
                <i class="fa fa-circle-o nav-icon"></i>
                <p>Role</p>
            </a>
        </li>
        <li class="nav-item">
            <a href="{{ route('users.roles_permission') }}" class="nav-link">
                <i class="fa fa-circle-o nav-icon"></i>
                <p>Role Permission</p>
            </a>
        </li>
        <li class="nav-item">
            <a href="{{ route('users.index') }}" class="nav-link">
                <i class="fa fa-circle-o nav-icon"></i>
                <p>Users</p>
            </a>
        </li>
    </ul>
</li>
@endrole
​
...

Note: Apit menu yang akan dibatasi khusus untuk user level tertentu dengan directive @role.

Untuk menu Manajemen Products ubah menjadi:

...
​
@if (auth()->user()->can('show products') || auth()->user()->can('delete products') || auth()->user()->can('create products'))
<li class="nav-item has-treeview">
    <a href="#" class="nav-link">
        <i class="nav-icon fa fa-server"></i>
        <p>
            Manajemen Produk
            <i class="right fa fa-angle-left"></i>
        </p>
    </a>
    <ul class="nav nav-treeview">
        <li class="nav-item">
            <a href="{{ route('kategori.index') }}" class="nav-link">
                <i class="fa fa-circle-o nav-icon"></i>
                <p>Kategori</p>
            </a>
        </li>
        <li class="nav-item">
            <a href="{{ route('produk.index') }}" class="nav-link">
                <i class="fa fa-circle-o nav-icon"></i>
                <p>Produk</p>
            </a>
        </li>
    </ul>
</li>
@endif
​
...

Penjelasan: Menu diatas hanya bisa diakses oleh user yang memiliki permission yang telah disebutkan. Kamu dapat menambahkan ataupun mengurangi rules yang dapat mengakses menu tersebut 

Kesimpulan

Role & permission atau dikenal sebagai authorization untuk mengatur setiap level user dalam mengakses sebuah aplikasi. Schema yang digunakan bersifat dinamis sesuai dengan kebutuhan. Sepanjang artikel ini kita telah membahasnya secara kompleks meski belum sepenuhnya dibahas, diantaranya bagaimana membuat custom exception untuk warning yang dihasilkan. Semua itu akan kita bahas pada chapter lainnya.

Untuk dokumentasi code dari artikel ini dapat kamu lihat di Github.

Category:
Share:

Comments