Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here

Adding Spatie/Laravel-Permission Package

Premium
4:50

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-permission
php 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...

The Full Lesson is Only for Premium Members

Want to access all of our courses? (31 h 16 min)

You also get:

55 courses
Premium tutorials
Access to repositories
Private Discord
Get Premium for $129/year or $29/month

Already a member? Login here

Comments & Discussion

DO
David O'Connell ✓ Link copied!

I'm not sure how clear this line is:

Note: This is a simplified example. Spatie recommends you check Permissions instead of Roles in these scenarios!

from the docs it says:

It's better to assign permissions to Roles, and then assign Roles to Users.

M
Modestas ✓ Link copied!

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 Assistant with permission to create_users. You add the check of assistant role to your code - everything is okay. But then, you drop the create_users permission 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! :)

DO
David O'Connell ✓ Link copied!

perfect, thanks for the detailed reply

��
Дмитрий Исаенко ✓ Link copied!

If you'll get the error that "guard_name" can't be null, please add method "->nullable()" to the certain Spatie-migration. And remember that you need to use Spatie Role model inside of the RoleSeeder: use Spatie\Permission\Models\Role;

RA
Richard A. Hoyle ✓ Link copied!

Question can this be used in Laravel Livewire v3 and if so, can you do a class showing us how to set it up using the tasks as an example?

M
Modestas ✓ Link copied!

Livewire usage does not change much.

If you need to prevent specific actions - then you add the gate/policy checks inside the class. If you need to hide the component - you can wrap the whole component in @can and that will be taken care of

We will see if this is worth doing, but at the moment - it would be a little bit too long to add to a comment

RA
Richard A. Hoyle ✓ Link copied!

Thank you verry much I will try to do this but I would verry much appreciate you doing a class on what we need to do to make sure that this works right.