Reputation: 87
Laravel 10 app, with Jetstream/Inertia/Vue 3.
I use laravel-spatie-permissions-vue to deal with user permissions and roles, which are assigned ok. Currently I am using default Jetstream views, so as the user logs in the dashboard is shown, where I put this on Components/Welcome.vue:
<div v-if="is('administrator')">is administrator.</div>
<div v-if="is('user')">is user.</div>
For testing purposes there are two roles, administrator and user, and there are some permissions too, but as is the same problem for them both let's stick with roles only. The problem is, the first time you login, let's go with user for example, nothing is shown, which is wrong. If you refresh the page, 'is user.' will appear, as it should. Then, if you log out and log in with administrator, 'is user.' will be shown (incorrect), but if you refresh the page 'is administrator.' will be shown (correct). I'm getting the same behavior in development, with valet and 'npm run dev', than when deployed to an apache server and 'npm run build' is executed.
I followed all the instructions at https://github.com/geowrgetudor/laravel-spatie-permissions-vue .
I've searched for two days, but as I haven't found anybody with the same problem, I guess I made some silly mistake. I think the problem should be related to the app.blade.php part, and perhaps it has to be something cache related, but haven't found anything following that road, so no clues at this point.
What I tried (only related steps):
laravel new application
composer require laravel/jetstream
php artisan jetstream:install inertia
npm i @vitejs/plugin-vue
npm install -D tailwindcss postcss autoprefixer
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
composer require geowrgetudor/laravel-spatie-permissions-vue
use SpatiePermissionVue\Traits\RolesPermissionsToVue;
class User extends Authenticatable
{
// ...
use RolesPermissionsToVue;
}
import RolesPermissionsToVue from "../../vendor/geowrgetudor/laravel-spatie-permissions-vue/src/js";
// createInertiaApp, right after '.use(ZiggyVue, Ziggy)':
.use(RolesPermissionsToVue)
<script type="text/javascript">
window.vueSpatiePermissions = {!! auth()->check() ? auth()->user()->getRolesPermissionsAsJson() : 0 !!}
</script>
added this this on Components/Welcome.vue:
<div v-if="is('administrator')">is administrator.</div>
<div v-if="is('user')">is user.</div>
and I was expecting 'is administrator.' or 'is user.' will be shown once a user logs in without having to refresh the page.
The user has its roles and permissions correctly assigned, and it can be checked adding this code in the share() function at HandleInertiaRequests:
$permissions = Auth::check() ? Auth::user()->permissions->pluck('name') : "";
$roles = Auth::check() ? Auth::user()->roles->pluck('name') : "";
return array_merge(parent::share($request), [
'auth.user.permissions' => $permissions,
'auth.user.roles' => $roles
]);
and then adding {{ $page.props.auth.user.roles }} or {{ $page.props.auth.user.permissions }} at the view, so that's not the problem.
Any help would be appreciated. Thanks.
In the end, thanks to cengsemihsahin's response I changed how I was approaching this, which in the end took me to How to Add Roles and Permission to Laravel Fortify + Inertia + vue? where I ended using Viduranga's approach, as in this way there's no need of laravel-apatie-permissions-vue or laravel-permission-to-vuejs, only change share() function in HandleInertiaRequests (here's mine):
public function share(Request $request): array
{
$permissions = Auth::check() ? Auth::user()->permissions->mapWithKeys(function($pr){ return [$pr['name'] => true]; }) : [];
$roles = Auth::check() ? Auth::user()->roles->mapWithKeys(function($pr){ return [$pr['name'] => true]; }) : [];
return array_merge(parent::share($request), [
'auth.user.permissions' => $permissions,
'auth.user.roles' => $roles
]);
}
then you can use this in your view:
v-if="$page.props.auth.user.roles['administrator']"
I think this one is a better solution than the one I was using for my project.
Upvotes: 0
Views: 2134
Reputation: 1144
First you need to know: The window.Laravel
or window.vueSpatiePermissions
variables that you assign in the app.blade.php
file are assigned when your vue application is created. So, changing the routes in the browser on your vue application or making a login or logout via the api will not change your `app.blade.php` view or not reload.
So what is the solution and what have I done for my own application?
First I did these using this [https://github.com/ahmedsaoud31/laravel-permission-to-vuejs][1] :
In **User** model:
use LaravelPermissionToVueJS;
In app.blade.php
<script type="text/javascript">
window.Laravel = {
csrfToken: "{{ csrf_token() }}",
jsPermissions: {!! auth()->check() ? auth()->user()->jsPermissions() : 0 !!}
};
</script>
In main.js (when creating app)
import App from './App.vue';
import LaravelPermissionToVueJS from 'laravel-permission-to-vuejs';
const app = createApp(App);
app.use(LaravelPermissionToVueJS);
app.mount('#app');
In my solution (in my own application), the page is reloaded as the page is redirected by providing an HTTP request on the browser at every login or logout action.
This is an unnecessary and unprofessional solution. Other than that, I will explain the solution you can do again a little below.
My login.vue
(in the shortest for example)
<form class="d-none" method="post" action="/auth/login">
<input type="hidden" name="_token" :value="csrf">
<input type="hidden" name="username" :value="user.username">
<input type="hidden" name="password" :value="user.password">
<button ref="submitLoginForm" class="d-none"></button>
</form>
<form class="text-start" @submit="login">
<div class="form">
<div id="username-field" class="field-wrapper input">
<input type="text" name="username" class="form-control"
required v-model="user.username"
placeholder="username" />
</div>
<div id="password-field" class="field-wrapper input mb-2">
<input
:type="show_password ? 'text' : 'password'"
v-model="user.password"
name="password"
class="form-control"
required
placeholder="passphrase"
/>
</div>
<button type="submit" class="btn btn-primary">login</button>
</div>
</form>
<script>
export default {
data() {
return {
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
user: {
username: '',
password: '',
remember: false,
}
}
},
methods: {
login(e) {
e.preventDefault();
store.dispatch('login', this.user);
this.$refs.submitLoginForm.click();
}
}
}
</script>
You can use the composition api, you can take a reference with
defineExpose
and submit the form.
Then the page is automatically refreshed and login is provided on both the api and the web side. There is a reason I do this specific to my own application.
window.Echo.private('permissions').listen('.user_permissions_changed', (e) => {
console.log(e);
window.Laravel.jsPermissions.permissions = e.user_permissions;
});
Because of I didn't have time, I couldn't go into full details. If you still have a question, please write it as a comment.
Upvotes: 1