Now let's work on restricting the permissions on the front end. We did secure everything on the back-end with the $this->authorize()
method in Controllers, but we also need to visually hide the menu items and buttons depending on the permissions.
For example, non-admin users shouldn't be able to see the top menu "Restaurants", right?
First, let's pass all the permissions to Vue components via the same Middleware HandleInertiaRequests
we used in the previous lesson.
app/Http/Middleware/HandleInertiaRequests.php:
class HandleInertiaRequests extends Middleware{ public function share(Request $request): array { return array_merge(parent::share($request), [ 'auth' => [ 'user' => $request->user(), 'permissions' => $request->user()?->permissions() ?? [], ], // ... ]); }}
And now, we can access that array of permissions via $page.props.auth.permissions
, so why don't we check that array contains the correct permission with the v-if
statement?
resources/js/Layouts/AuthenticatedLayout.vue:
<!-- Navigation Links --><div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex"> <NavLink v-if="$page.props.auth.permissions.includes('restaurant.viewAny')" :href="route('admin.restaurants.index')" :active="route().current('admin.restaurants.index')">Restaurants </NavLink></div>
And that's it! Now, if I register as a non-admin user, I will not see the menu item of "Restaurants".
So, our goal for the front-end show/hide of buttons and links is to add v-if
statements to all <Link>
and <NavLink>
components.
And to make our lives easier, let's create a "shortcut" method called can(permission_name)
, to avoid repeating this longer $page.props.auth.permissions.includes
every time.
I will create a JS file in the Support
subfolder, like a Service class in Laravel.
resources/js/Support/can.js
import { usePage } from '@inertiajs/vue3' export const Can = { install: (v) => { const page = usePage() const can = (permission) => { return page.props.auth.permissions.includes(permission) } v.mixin({ methods: { can } }) }}
Then, we need to import it into the main app.js
:
resources/js/app.js
import { createInertiaApp } from '@inertiajs/vue3'import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m'import { Can } from '@/Support/can.js' return createApp({ render: () => h(App, props) }) .use(plugin) .use(ZiggyVue, Ziggy) .use(Can) .mount(el) },
And then we can replace that long $page.props.auth.permissions.includes()
with can()
resources/js/Layouts/AuthenticatedLayout.vue:
<NavLink v-if="$page.props.auth.permissions.includes('restaurant.viewAny')" v-if="can('restaurant.viewAny')"
Finally, let's apply the same logic to a few more links on the Restaurants page. Only admins can create and edit restaurants, so let's add those permissions to show those links.
resources/js/Pages/Admin/Restaurants/Index.vue:
<Link class="btn btn-primary" v-if="can('restaurant.create')" :href="route('admin.restaurants.create')"> Add New Restaurant</Link> // ... <td> <Link v-if="can('restaurant.update')" :href="route('admin.restaurants.edit', restaurant)" class="btn btn-secondary" > Edit </Link></td>
Great, we took care of the permissions on the front end, and now we have a global can()
method which we will use on other pages.