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...
I'm not sure how clear this line is:
from the docs it says:
Yes, you assign roles with permissions to the users, since that is a better workflow. But once that is done - you still check for permission, not the role level. Since roles can have permissions removed.
Imagine this scenario:
You have a role
Assistantwith permission tocreate_users. You add the check ofassistantrole to your code - everything is okay. But then, you drop thecreate_userspermission from the role.Now you have to go back into your code and modify the condition. If you have checked for the permission - it would've been fine immediately and you would not have to change any code.
Hope that clarifies what we and the docs meant! :)
perfect, thanks for the detailed reply