Table of Contents

Penjelasan

Dalam penggunaan aplikasi sebenarnya kita akan membutuhkan permission misal untuk akses misal create, read, update, delete ke suatu Modul baik berdasarkan UserId maupun UserRoles. 

Disini kita akan menggunakan suatu library bernama Spatie untuk memanage Roles & Permission tersebut berdasarkan User ataupun Rolesnya.

Asumsi :

  1. Laravel sudah diinstall
  2. UI Auth sudah diinstall(Breeze ataupun yang lainnya)
  3. Composer sudah terinstall di mesin anda
  4. UI CRUD Post sudah dibuat
Installasi
  1. Library ini nantinya akan berkaitan langsung dengan User Model
  2. Library ini akan mengcreate file config/permission.php. Jika kamu sudah memiliki file dengan nama itu kamu harus mengganti nama atau menghapusnya terlebih dahulu.

Lanjut installasi menggunakan Composer

				
					composer require spatie/laravel-permission
				
			

publish migrasi dan config/permission.php config dengan perintah :

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

Pada log kita bisa melihat ada file migration baru untuk table permissions dan role dari package spatie laravel-permission. Selanjutnya kita run migration. Namun sebelumnya kita clear cache dahulu.

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

Setelah kita run migration, kita bisa lihat ada beberapa table baru dari package ini dan juga table users di database.

Penggunaan

Untuk menggunakan spatie laravel-permission, kita bisa menambahkan trait Spatie\Permission\Traits\HasRoles ke dalam User model. Buka app/Models/User.php, lalu kita tambahkan statement use di dalam class User model.

				
					...
use Spatie\Permission\Traits\HasRoles;
...
class User extends Authenticatable
{
    use HasRoles, HasApiTokens, HasFactory, Notifiable;
...
				
			
Menambahkan Simulasi Permission

Untuk melakukan test, kita coba buat simulasi penambahan Permission menggunakan Seeder.

				
					php artisan make:seeder PermissionDemo1Seeder
				
			
				
					<?php

namespace Database\Seeders;

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

class PermissionDemo1Seeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // reset cahced roles and permission
        app()[PermissionRegistrar::class]->forgetCachedPermissions();

        // create permissions
        Permission::create(['name' => 'view posts']);
        Permission::create(['name' => 'create posts']);
        Permission::create(['name' => 'edit posts']);
        Permission::create(['name' => 'delete posts']);
        Permission::create(['name' => 'publish posts']);
        Permission::create(['name' => 'unpublish posts']);

        //create roles and assign existing permissions
        $writerRole = Role::create(['name' => 'writer']);
        $writerRole->givePermissionTo('view posts');
        $writerRole->givePermissionTo('create posts');
        $writerRole->givePermissionTo('edit posts');
        $writerRole->givePermissionTo('delete posts');

        $adminRole = Role::create(['name' => 'admin']);
        $adminRole->givePermissionTo('view posts');
        $adminRole->givePermissionTo('create posts');
        $adminRole->givePermissionTo('edit posts');
        $adminRole->givePermissionTo('delete posts');
        $adminRole->givePermissionTo('publish posts');
        $adminRole->givePermissionTo('unpublish posts');

        $superadminRole = Role::create(['name' => 'super-admin']);
        // gets all permissions via Gate::before rule

        // create demo users
        $user = User::factory()->create([
            'name' => 'Andi',
            'email' => 'andi@ombagoes.com',
            'password' => bcrypt('12345678')
        ]);
        $user->assignRole($writerRole);

        $user = User::factory()->create([
            'name' => 'Susi',
            'email' => 'susi@ombagoes.com',
            'password' => bcrypt('12345678')
        ]);
        $user->assignRole($writerRole);

        $user = User::factory()->create([
            'name' => 'Bambang',
            'email' => 'bambang@ombagoes.com',
            'password' => bcrypt('12345678')
        ]);
        $user->assignRole($adminRole);

        $user = User::factory()->create([
            'name' => 'Bagoes',
            'email' => 's.bagoes@gmail.com',
            'password' => bcrypt('12345678')
        ]);
        $user->assignRole($superadminRole);
    }
}

				
			

Penjelasan

Post memiliki permission :

  1. view
  2. create
  3. edit
  4. delete
  5. publish
  6. unpublish

Terdapat 3 role yang dapat mengakses Post yaitu :

  1. writer dengan akses : view,  create ,edit,  dan delete
  2. admin dengan akses : writer + publish, dan upublish
  3. super-admin akses : admin karena dia bebas.

Masing2 role memiliki anggota yaitu :

  1. writer : Andi dan Susi
  2. admin : Bambang
  3. super-admin : Bagoes

Langsung jalankan Seeder sekalian refresh database untuk memastikan kode yang ditulis sebelumnya tidak ada kesalahan di antara kita.

				
					php artisan migrate:fresh --seed --seeder=PermissionDemo1Seeder
				
			

Setelah command di atas kita jalalankan, kita bisa lihat hasil sample data untuk permissions, roles dan juga users.

Grant akses Super Admin

Di class PermissionDemo1Seeder, kita menambahkan role super-admin dan di dalamnya dengan permission kosong untuk role tersebut. Hal tersebut dikarenakan kita menyiapkan super-admin sebagai role utama atau root dimana role tersebut dapat mengakses segalanya.

Di sini kita akan coba memberikan akses super admin melalui gate di dalam AuthServiceProvider. Buka file app/Providers/AuthServiceProvider.php, lalu kita modifikasi method boot().

				
					<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The model to policy mappings for the application.
     *
     * @var array<class-string, class-string>
     */
    protected $policies = [
        // 'App\Models\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

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

				
			

Pada baris kode di atas, ketika super admin login ke dalam web, semua pengecekan permission yang memanggil function call() atau @can() akan bernilai true. Ya, super admin bebas mau melakukan apa saja.

Menggunakan permission Middleware

Untuk menggunakan middleware dari spatie laravel-permission, kita harus mendefinisikan middleware-nya terlebih dahulu. Buka file app/Http/Kernel.php, lalu cek sekitar baris 56 terdapat $routeMiddleware, properties dari class Kernel. Kita tambahkan route middleware untuk role, permission dan role_or_permission dari spatie laravel-permission.

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

Pada artikel sebelumnya kita pernah membuat CRUD Post. Disana ada tampilan untuk melakukan CRUD pada Module Post. Sekarang kita akan menambahkan beberapa action yaitu publish dan unpublish

View

resources/views/posts/index.blade.php

				
					@extends('layouts.app')

@section('content')
@if($message = Session::get('success'))

<div class="alert alert-success">
	{{ $message }}
</div>

@endif
<nav aria-label="breadcrumb">
  <ol class="breadcrumb">
    <li class="breadcrumb-item"><a href="#">Home</a></li>
    <li class="breadcrumb-item active" aria-current="page">{{ $title }}</li>
  </ol>
</nav>
<div class="card">
	<div class="card-header">
		<div class="row">
			<div class="col col-md-6"><b>{{ $title }}</b></div>
			<div class="col col-md-6">
                @can('create posts', Post::class)
				    <a href="{{ route('posts.create') }}" class="btn btn-success btn-sm float-end">Add</a>
                @endcan
			</div>
		</div>
	</div>
	<div class="card-body">
		<table class="table table-bordered">
			<tr>
				<th>Title</th>
				<th>Content</th>
				<th>Category</th>
				<th>Action</th>
			</tr>
			@if(count($data) > 0)

				@foreach($data as $row)

					<tr>
						<td>{{ $row->title }}</td>
						<td>{{ substr($row->content,1,100) }}</td>
						<td>{{ $row->category->name }}</td>
						<td>
                            @can('edit posts', Post::class)
						        <a href="{{ route('posts.edit', $row->id) }}" class="btn btn-warning btn-sm">Edit</a>
                            @endcan
                            @can('delete posts', Post::class)
                                <form method="post" action="{{ route('posts.destroy', $row->id) }}">
                                    @csrf
                                    @method('DELETE')
                                    <input type="submit" class="btn btn-danger btn-sm" value="Delete" />
                                </form>
                            @endcan
                            @can('publish posts', Post::class)
                                <form onsubmit="return confirm('Publish post ini?');" action="{{ route('post.publish', $post->id) }}" method="POST">
                                    @csrf
                                    @method('PUT')
                                    <button type="submit" class="btn btn-sm btn-info">Publish</button>
                                </form>
                            @endcan
                            @can('unpublish posts', Post::class)
                                <form onsubmit="return confirm('Unpublish post ini?');" action="{{ route('post.unpublish', $post->id) }}" method="POST">
                                    @csrf
                                    @method('PUT')
                                    <button type="submit" class="btn btn-sm btn-info">Unpublish</button>
                                </form>
                            @endcan
						</td>
					</tr>

				@endforeach

			@else
				<tr>
					<td colspan="5" class="text-center">No Data Found</td>
				</tr>
			@endif
		</table>
        <div class="py-2">
		    {!! $data->appends(['sort','title'])->links() !!}
        </div>
	</div>
</div>
@endsection('content')

				
			
Controller

app/Http/Controllers/PostController.php

				
					<?php

namespace App\Http\Controllers;

use App\Models\Post;
use App\Models\Category;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $title= "Post";
        $data = Post::with('category')->orderBy('id','desc')->paginate(10);
        return view('dashboard.modules.post.standard.index',compact('title','data'));
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        $title = 'Post';
        $categories = Category::all();
        return view('dashboard.modules.post.standard.create', compact('title','categories'));
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required',
            'category_id' => 'required|exists:App\Models\Category,id'
        ]);
        Post::create([
            'title' => $request->title,
            'slug' => \Str::slug($request->title),
            'content' => $request->content,
            'category_id' => $request->category_id,
        ]);

        return redirect()->route('posts.index')->with('status', 'Post Created Successfully');
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function show(Post $post)
    {
        return view('dashboard.modules.post.standard.show', compact('post'));
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function edit(Post $post)
    {
        $title = 'Post';
        $categories = Category::all();
        return view('dashboard.modules.post.standard.edit', compact('title','categories', 'post'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Post $post)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required',
            'category_id' => 'required|exists:App\Models\Category,id',
        ]);
        $post->title = $request->title;
        $post->slug = \Str::slug($request->title);
        $post->content = $request->content;
        $post->category_id = $request->category_id;
        $post->save();

        return redirect()->route('posts.index')->with('status', 'Post Updated Successfully');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function destroy(Post $post)
    {
        $post->delete();
        return redirect()->route('posts.index')->with('status', 'Post Delete Successfully');
    }

    public function publish(Post $post)
    {
        return redirect()->route('posts.index')->with('status', 'Berhasil di Publish');
    }

    public function unpublish(iPost $post)
    {
        return redirect()->route('posts.index')->with('status', 'Berhasil di Unpublish');
    }
}

				
			
Route

routes/web.php

				
					...
Route::middleware('auth')->group(function () {
    Route::resource('posts', \App\Http\Controllers\PostController::class);
    Route::put('post/{id}/publish', [\App\Http\Controllers\PostController::class, 'publish'])->name('post.publish');
    Route::put('post/{id}/unpublish', [\App\Http\Controllers\PostController::class, 'unpublish'])->name('post.unpublish');
    ...
});
				
			
Login Role Writer

Login dengan user Susi atau Andi, maka tampilannya akan seperti berikut.

Login Role Admin

Logout lalu login kembali dengan user Bambang, maka tampilannya akan seperti berikut.

User Interface

Untuk mempermudah pengaturannya kita memerlukan UI, dimana spatie tidak menyediakan tampilannya. Tetapi untungnya banyak yang menyediakan UI nya salah satunya bisa dilihat disini.

https://github.com/LaravelDaily/laravel-permission-ui

Berikut capture tampilannya :

 

Tinggal disesuaikan dengan kreatifitas anda untuk di masukan ke dalam template anda sendiri.