UPDATE: You can do the same as below using this package geowrgetudor/laravel-spatie-permissions-vue. It is a fork I made from the original package. Adds support for server-side rendering (SSR) and doesn't require installing a npm package (everything is built in).
When in comes to Laravel packages, the guys at Spatie are probably the kings. They have hundreds of free packages you can pick from and use into your projects. Hats down for their commitment and contribution to this beautiful ecosystem.
Now let's talk about permissions because if you're building a large Laravel app you definitely gonna need them. As a quick example, let's think of an admin panel built with Vue that needs different user roles where each role has specific permissions.
Of course you can build all this functionality yourself, but why reinvent the wheel? There's a neat package from Spatie called spatie/laravel-permission which makes things really simple.
Installation and setup
All you have to do is to install it and do a bit of configuration like publishing the vendor files (config and migration) plus some trait:
composer require spatie/laravel-permission
# Publish the vendor's config file and migration
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
# Clear your config cache so the package can pick up the published filed
php artisan config:clear
# Run the migration
php artisan migrate
The next step is to add the package's trait to your user model and you're ready to roll:
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasRoles;
}
By default, the package comes with a super-admin
role published into your database by the default migration. You can use that of course, but you also can add your own roles and permissions.
Adding new roles and permissions
So let's create 2 roles and assign them some permissions. You can do this in a migration, seeder, artisan command, some UI interface, whatever suits you best.
The package comes with some premade artisan commands you can use to create roles and permissions (you can check their docs for that), but for the sake of this demo, I'm going to use tinker
.
# Create an editor role
$roleEditor = \Spatie\Permission\Models\Role::create(['name' => 'editor']);
# Create a supervisor role
$roleSupervisor = \Spatie\Permission\Models\Role::create(['name' => 'supervisor']);
# Now lets create permissions and assign them to our freshly created roles
$permissions = [
'can-view-posts',
'can-edit-posts',
'can-publish-posts',
'can-deleted-posts',
'can-view-activity',
'can-export-activity'
];
foreach($permissions as $permissionName) {
$permission = \Spatie\Permission\Models\Permission::create(['name' => $permissionName]);
if(in_array($permissionName, ['can-view-posts', 'can-edit-posts', 'can-publish-posts', 'can-deleted-posts'])) {
$roleEditor->givePermissionTo($permission);
} else {
$roleSupervisor->givePermissionTo($permission);
}
}
Now, that we have all the permissions in place for each role, we can start using them in our controllers, services, routes, templates, etc.
Let's talk about templates
By default, this package comes with some handy Blade directives. You can use @can
, @role
, @hasrole
, @hasanyrole
and many more directives to render whatever you need based on roles and permissions. This is great, but these are only available in Blade.
How can we create something similar in Vue? The process will be like this:
- We need to expose all the roles and permissions to the frontend
- We need to defines some similar functions
Luckily, you don't have to do this from scratch. You can use laravel-permission-to-vuejs which is a javascript package and ahmedsaoud31/laravel-permission-to-vuejs which is a composer package. These 2 packages are great and are exactly what we need to use the same roles and permissions into our Vue application.
First, we need to install the server side package and add another trait to our User
model:
composer require ahmedsaoud31/laravel-permission-to-vuejs=dev-master
Now lets add the trait:
use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;
use LaravelAndVueJS\Traits\LaravelPermissionToVueJS;
class User extends Authenticatable
{
use HasRoles;
use LaravelPermissionToVueJS;
}
Next, we'll need to expose the roles and permissions to the frontend. To do this we need to attach a Laravel
property to the window
. You can add this code into your main Blade template which loads everything for your frontend:
<script type="text/javascript">
window.Laravel = {
csrfToken: "{{ csrf_token() }}",
jsPermissions: {!! auth()->user()?->jsPermissions() !!}
}
</script>
The last step is to install the Vue javascript package, which also supports Vue 3:
npm install --save laravel-permission-to-vuejs
# Instruct Vue to use it
// ...
import LaravelPermissionToVueJS from 'laravel-permission-to-vuejs'
const app = createApp(App)
app.use(LaravelPermissionToVueJS)
// ...
Now we can use similar functions to Spatie's blade directives like can()
to check for a specific permission and is()
to check for specific roles. Here's a short example:
<div v-if="can('can-view-posts')">
Only users with this permission can view this DIV.
</div>
<div v-if="is('supervisor')">
Only users with this role can view this DIV.
</div>
We can also pass multiple permissions or roles to check against. Example: can('can-view-posts | can-edit-posts')
.
That's pretty much it. Now you can enjoy using the same permissions you are using in the backend without creating a lot of mess in your Vue templates.
Top comments (4)
It's working for me but you need to refresh the page for it to work. If for instance you log in as another user role, log out, and log back in as another user role, the role and permissions seems to not refresh.
Probably late but I fixed this issue with:
in addition to using:
<script type="text/javascript">
window.Laravel = {
csrfToken: "{{ csrf_token() }}",
jsPermissions: {!! auth()->check()?auth()->user()->jsPermissions():0 !!}
}
</script>
in app.js
axios.get('/get-permissions').then(
response => {
window.Laravel.jsPermissions = response.data;
}
);
in laravel route
Route::get('/get-permissions', function () {
return auth()->check()?auth()->user()->jsPermissions():0;
});
As the issue comes from the permissions being loaded on the first time you load a page only.
With this solution every time you visit a route you call a get-permissions api and check for the permissions, also you might consider only refreshing the permissions on login or somehow call the api only when needed to increase the performance although I don't really think its that important.
This method is mentioned in the package Readme here
not working in composition api