Roles and Permissions in Laravel Jetstream (Inertia.js) using Spatie Package
How to use roles and permissions in a Laravel Jetstream (Inertiajs) application using the Spatie Permission package.
How to use Roles and Permissions in Laravel Jetstream (Inertiajs) using the Spatie Package
Using roles and permissions in Laravel can seem daunting at first. But when you break it down step by step its actually a very simple process.
Whats included in this example?
In this example we are going to create 2 roles.
- ‘admin’ role
- ‘standard’ role
We are then going to create 4 permissions.
- ‘write articles’
- ‘read articles’
- ‘edit articles’
- ‘delete articles’
We will then assign these permissions to the relevant roles. The ‘admin’ role will have all 4 permissions, while the ‘standard’ role will only have the ‘read articles’ role. We will then assign the roles to users within our application. Finally we will look at how to display content within our application based on the permissions held by the currently authenticated user. It sounds like a lot but it’s very simple when you break it down!
Setup Spatie Permission Package
Install the Spatie Laravel-permission package
The first thing we need to do is install the Spatie Permission package. Run the following command in the root of your Laravel project:
composer require spatie/laravel-permission
Publish the migration and config file
Now we need to publish the migration and config file. Run the following command:
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
Run the migration
Now we need to run the migration. Run the following artisan command:
php artisan migrate
Amend your User Model
We now need to add a the HasRoles trait to our User model. Open your User.php file and make sure it includes the following:
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasRoles;
// ...
}
Create your Roles and Permissions
The Spatie package is now setup and ready to use. Now we need to actually create the roles and permissions that we want to use.
Create Roles and Permissions in Laravel using a seeder
In this example we are going to use a seeder to create our roles and permissions. This is useful in some cases and not so useful in others. This method will allow us to create the roles and permissions for our application when we run our seeder command, but it won’t help us if we need to manage the roles and permissions. If you want to create roles and permissions from an admin page within your application then you can still use the methods that I am going to show you. The difference will be that you will need to use them in a controller. But lets look at how to create them using a seeder.
Generate your Roles and Permission Seeder
Run the following command to create your seeder.
php artisan make:seeder --class=RolesSeeder
You should now have the file RolesSeeder.php in your seeders folder (found at project_root/database/seeders/RolesSeeder.php
). Open your newly created RolesSeeder.php file. Add the following to the top of the file:
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
// ...
We are now able to create our roles and permissions.
Create Roles
Look at your RolesSeeder.php file. You will notice there is a function named public function run()
. This is where we are going to include all the logic for creating our roles and permissions, and assigning our permissions to our roles. Now we are going to write the code that will create our ‘admin’ and ‘standard’ roles when we run our seeder. Add the following to the ‘run’ function:
public function run()
{
$role_admin = Role::create(['name' => 'admin']);
$role_standard = Role::create(['name' => 'standard']);
// we will add more to this in a second
}
Create Permissions
Now we need to write the code that will create our permissions. Again, add to the ‘run’ function:
public function run()
{
$role_admin = Role::create(['name' => 'admin']);
$role_standard = Role::create(['name' => 'standard']);
$permission_read = Permission::create(['name' => 'read articles']);
$permission_edit = Permission::create(['name' => 'edit articles']);
$permission_write = Permission::create(['name' => 'write articles']);
$permission_delete = Permission::create(['name' => 'delete articles']);
// we will add more to this in a second
}
Assign permissions to roles in Laravel
Now we need to assign the permissions to the relevant roles. In our example we are going to give the ‘admin’ role all 4 permissions, and give the ‘standard’ role the permission to ‘read articles’ only. Go back to the RolesSeeder.php file and add to the ‘run’ function:
public function run()
{
$role_admin = Role::create(['name' => 'admin']);
$role_standard = Role::create(['name' => 'standard']);
$permission_read = Permission::create(['name' => 'read articles']);
$permission_edit = Permission::create(['name' => 'edit articles']);
$permission_write = Permission::create(['name' => 'write articles']);
$permission_delete = Permission::create(['name' => 'delete articles']);
$permissions_admin = [$permission_read, $permission_edit, $permission_write, $permission_delete];
$role_admin->syncPermissions($permissions_admin);
$role_standard->givePermissionTo($permission_read);
}
Assign Role to users in Laravel
We are not going to run this seeder just yet, but when we do it will create our roles, create our permissions, and assign the permissions to the roles. We now need to look at how to assign the roles to users. There are 2 ways in which we are going to do this. By assigning newly created users that sign up to our website via the registration form a role when they sign up, and by assigning our seeded users a role when they are created. Lets first look at assigning roles to our seeded users.
Assign role to user using user seeder
If you haven’t already, create a user seeder. We are now going to edit the ‘run’ function in our UserSeeder.php file. Add the following:
public function run()
{
//
$users = [
[
'name' => 'Admin',
'email' => 'admin@codingoblin.com',
'password' => 'password',
'role' => 'admin',
],
[
'name' => 'John',
'email' => 'john@codingoblin.com',
'password' => 'password2',
'role' => 'standard',
]
];
foreach($users as $user) {
$created_user = User::create([
'name' => $user['name'],
'email' => $user['email'],
'password' => Hash::make($user['password']),
]);
$created_user->assignRole($user['role']);
}
}
Now go to your DatabaseSeeder.php file and add the following:
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Database\Seeders\UserSeeder;
use Database\Seeders\RolesSeeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call([
RolesSeeder::class,
UserSeeder::class,
]);
}
}
Now we just need to run our seeders. To just run our seeders run the following command:
php artisan db:seed
This probably won’t be much use to us as we have most likely already previously run this command and therefore created our users already. If you wish to drop all tables in your database, start again fresh and run the seeders, run the following command instead:
php artisan migrate:fresh --seed
You should now have your roles and permissions created, permissions assigned to relevant roles, users created, roles assigned to relevant users.
Assign role to user when the user creates an account / signs up
We have seen how to assign roles to users using seeders, but now we need to look at assigning roles to the users who sign up to our website through our registration form. The good news is this is really quick and easy. In our example, we will assign new users the role ‘standard’. Open your CreateNewUser.php file found at your_project/app/Actions/Fortify/CreateNewUser.php
. Amend the ‘create’ function so that it looks like this:
public function create(array $input)
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => $this->passwordRules(),
'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['accepted', 'required'] : '',
])->validate();
$created_user = User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
]);
$created_user->assignRole('standard');
return $created_user;
}
And that’s it. I told you it was simple. Now when a new user registers they will automatically be assigned the ‘standard’ role.
How to display content dependant on the role of the user
We are now in a position where we are able to make use of our roles and permissions to show and hide content depending on the role of the user. To do this, we are going to make use of props. We will send the roles and permissions that the user possesses along with each request. This means we can check a user has permission to view something before we show it to them.
Add roles and permissions to page props using inertiajs shared data
By default, out Laravel Jetstream application sends certain page props with every request. One of these is an object containing lots of information about the currently authenticated user. We are going to append the roles and permissions held by the user to this object, so we can also access this in our page views!
Add to HandleInertiaRequests.php Middleware
To add the roles and permissions to the page props, we need to add a couple of lines of code to the HandleInertiaRequests Middleware. You can find this file at your_project/app/Http/Middleware/HandleInertiaRequests.php
. Open this file and add the following to the ‘share’ function:
public function share(Request $request): array
{
return array_merge(parent::share($request), [
//
'user.roles' => $request->user() ? $request->user()->roles->pluck('name') : [],
'user.permissions' => $request->user() ? $request->user()->getPermissionsViaRoles()->pluck('name') : [],
]);
}
Obs: esta parte, depende muito da versao do laravel, se for o laravel 10 e preciso fazer desta forma, pois existe uma diferença em um Ziggy pacote adicionado para gerenciar as rotas javascript
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
use Tightenco\Ziggy\Ziggy;
class HandleInertiaRequests extends Middleware
{
/**
* The root template that is loaded on the first page visit.
*
* @var string
*/
protected $rootView = 'app';
/**
* Determine the current asset version.
*/
public function version(Request $request): ?string
{
return parent::version($request);
}
/**
* Define the props that are shared by default.
*
* @return array<string, mixed>
*/
public function share(Request $request): array
{
return [
...parent::share($request),
'user.roles' => fn () => $request->user() ? $request->user()->roles->pluck('name') : null,
'user.permissions' => fn () => $request->user() ? $request->user()->getPermissionsViaRoles()->pluck('name') : null,
'ziggy' => fn () => [
...(new Ziggy)->toArray(),
'location' => $request->url(),
],
];
}
}
Now, whenever an authenticated (logged in) user accesses a page on our website, we can see what roles and permissions they have by looking in the $page.props
. Lets see that in action!
Test to see if roles and permissions are attached to page props
We are now in a position where we can access the authenticated user’s roles and permissions in our page views (our Vue.js files). To see this in action, go to a page within your application (we will be using the default dashboard page that comes with Laravel Jetstream, and will be editing this file – your_project/resources/js/Components/Welcome.vue
. Add this code to your page template:
<template>
<div>
{{ $page.props }}
</div>
</template>
Save the file and go to the page in the browser (make sure you are using a logged in user with a role). You should see the following on your page:
Notice that we can see arrays containing the user’s roles and permissions.
Display content depending on permissions held by user
We are now in a position to actually use the roles and permissions to hide and show content on the page. To demonstrate this we will create 4 buttons on our page. You can do this on any page you like but if you are following along we will use the same file – your_project/resources/js/Components/Welcome.vue
. Open Welcome.vue, remove everything between the template tags replace it with this:
<template>
<div>
<div>
<div v-if="$page.props.user.permissions.includes('read articles')">
<button type="button">Read article</button>
</div>
<div v-if="$page.props.user.permissions.includes('write articles')">
<button type="button">Write article</button>
</div>
<div v-if="$page.props.user.permissions.includes('edit articles')">
<button type="button">Edit article</button>
</div>
<div v-if="$page.props.user.permissions.includes('delete articles')">
<button type="button">Delete article</button>
</div>
</div>
</div>
</template>
Above we have 4 buttons. Each button is wrapped in a div that is shown conditionally. Each if statement is saying, if the permission name is found within the permission array that is passed to the page as a prop within the user object prop, then show the button. If you are logged in as the ‘admin’ role, you will see all 4 buttons. If you are logged in as the ‘standard’ role you will just see the ‘read articles’ button. You have now successfully implemented roles and permission in your Laravel Jetstream (Inertiajs) application on the frontend! Congratulations. But what about protecting resources on the backend? Lets look at how to do this!