One of the most typical questions I received when preparing for this course was, "Will you use the Spatie Permissions package?"
And I thought, "Why not BOTH". So, first, I've shown you how to use gates/policies without any packages, and now it's time to show the "other side".
Starting Point
We will work on the same project but take its "earlier" point. Imagine we have Gates without Policies.
app/Http/Controllers/TaskController.php
class TaskController extends Controller{ public function index(): View { $tasks = Task::with('user')->get(); return view('tasks.index', compact('tasks')); } public function create(): View { Gate::authorize('create', Task::class); return view('tasks.create'); } public function store(Request $request): RedirectResponse { Gate::authorize('create', Task::class); Task::create($request->only('name', 'due_date') + ['user_id' => auth()->id()]); return redirect()->route('tasks.index'); } public function edit(Task $task): View { Gate::authorize('update', $task); return view('tasks.edit', compact('task')); } public function update(Request $request, Task $task): RedirectResponse { Gate::authorize('update', $task); $task->update($request->only('name', 'due_date')); return redirect()->route('tasks.index'); } public function destroy(Task $task): RedirectResponse { Gate::authorize('delete', $task); $task->delete(); return redirect()->route('tasks.index'); }}
And then we have @can
checks in the Blade file.
resources/views/tasks/index.blade.php
<div class="min-w-full align-middle"> @can('create', \App\Models\Task::class) <a href="{{ route('tasks.create') }}" class="underline">Add new task</a> <br /><br /> @endcan <table class="min-w-full border divide-y divide-gray-200"> <thead> ... </thead> <tbody class="bg-white divide-y divide-gray-200 divide-solid"> @foreach($tasks as $task) <tr class="bg-white"> ... <td class="px-6 py-4 text-sm leading-5 text-gray-900 whitespace-no-wrap"> @can('update', $task) <a href="{{ route('tasks.edit', $task) }}" class="underline">Edit</a> @endcan @can('delete', $task) | <form action="{{ route('tasks.destroy', $task) }}" method="POST" class="inline-block" onsubmit="return confirm('Are you sure?')"> @method('DELETE') @csrf <button type="submit" class="text-red-600 underline">Delete</button> </form> @endcan </td> </tr> @endforeach </tbody> </table></div>
Now, let's install the package and configure its roles.
Installing Spatie Permissions Package
We'll do everything according to basic official documentation.
composer require spatie/laravel-permissionphp artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
Then, we need to add a trait to our User Model and remove our own roles implementation:
app/Models/User.php
use Spatie\Permission\Traits\HasRoles; // ... class User extends Authenticatable{ use HasFactory, Notifiable; use HasRoles; // ... protected $fillable = [ 'name', 'email', 'password', 'role_id', ]; // ... public function role(): BelongsTo { return $this->belongsTo(Role::class); }}
We also removed the role_id
and the relationship we had in the beginning. It will all be saved in the...