Working with applications that have multiple roles can require you to have a Role Switcher to allow your user to act in a different role. For example, a user can be a doctor, but also a patient. In this practical example, we'll add role-switching to our navigation bar to allow this.
Setup
In our application, we have the following:
- Users table
- Roles table (provided by Laravel-permission) as PolyMorphic table
model_has_roles
- Pivot table for users and roles
Which will look like this in our database:
Step 1. Adding Current Role to Users Table
First, we need to add a current_role_id
column to our users table to remember what role the user picked last:
Migration
Schema::table('users', function (Blueprint $table) { $table->foreignId('current_role_id')->nullable()->constrained('roles')->after('email');});
After adding it, we need to make it fillable in our User
model and define a relationship:
app/Models/User.php
use Spatie\Permission\Models\Role; // ... protected $fillable = [ 'name', 'email', 'password', 'current_role_id']; // ... public function currentRole(): BelongsTo{ return $this->belongsTo(Role::class, 'current_role_id');}
Notice: You will have to update Factories, Tests, or Seeders to set a default role too.
Step 2. Role Switcher Controller and Route
Before working on the view, we must create a controller to handle the role switching. We'll call it SwitchRoleController
:
app/Http/Controllers/SwitchRoleController.php
use Spatie\Permission\Models\Role; // ... class SwitchRoleController extends Controller{ public function __invoke(Role $role) { abort_unless(auth()->user()->hasRole($role), 404); auth()->user()->update(['current_role_id' => $role->id]); return to_route('dashboard'); // Replace this with your own home route }}
Register it in your routes/web.php
file:
routes/web.php
// ... Route::middleware(['auth'])->group(function () { // ... Route::get('/switch-role/{role}', SwitchRoleController::class)->name('switch.role'); // ...}); // ...
Step 3. Role Switcher in Blade Menu
Now we can focus on adding the Switcher to our navigation bar.
We update the default Laravel Breeze top-right navigation menu, adding two sub-items:
- Information about the current role
- If user has multiple roles, showing all of them with the link to switch role
resources/views/layouts/navigation.blade.php
{{-- ... --}} <!-- Settings Dropdown --><div class="hidden sm:flex sm:items-center sm:ml-6"> <x-dropdown align="right" width="48"> {{-- ... --}} <x-slot name="content"> <x-dropdown-link href="#"> {{ __('Logged in as: :role', ['role' => auth()->user()->currentRole->name]) }} </x-dropdown-link> @if(auth()->user()->roles->count() > 1) @foreach(auth()->user()->roles->where('id', '!=', auth()->user()->current_role_id) as $role) <x-dropdown-link :href="route('switch.role', $role->id)"> {{ __('Switch to :name', ['name' => $role->name]) }} </x-dropdown-link> @endforeach @endif <hr/> {{-- ... --}} </x-slot> </x-dropdown></div> {{-- ... --}}
Loading our page will now show us the current role and a list of other roles we can switch to:
This is it! Users can now switch between active roles within your system and return to the same role when logging in. You can add more functionality to your application based on this role.
Bonus: Role-Based Menu Example
Want to show different menu based on the current role? Here's one possible approach:
resources/views/layouts/navigation.blade.php
{{-- ... --}} <!-- Navigation Links --><div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex"> @includeIf('layouts.menu.' . auth()->user()->currentRole->name)</div> {{-- ... --}}
So, we're including the Blade file with the role name in it. We're using the includeIf()
method which would also check if that file exists.
And we can create the partials for each role:
resources/views/layouts/menu/doctor.blade.php
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')"> {{ __('Dashboard') }}</x-nav-link><x-nav-link href="#"> {{ __('My patients') }}</x-nav-link><x-nav-link href="#"> {{ __('My calendar') }}</x-nav-link><x-nav-link href="#"> {{ __('My profile') }}</x-nav-link>
resources/views/layouts/menu/patient.blade.php
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')"> {{ __('Dashboard') }}</x-nav-link><x-nav-link href="#"> {{ __('My appointments') }}</x-nav-link><x-nav-link href="#"> {{ __('My health') }}</x-nav-link><x-nav-link href="#"> {{ __('My profile') }}</x-nav-link>
After this, loading our page will show us the correct navigation links based on the current role:
Doctor
Patient
What database design tool did you use?
This was an existing database view from DataGrip (jetbrains darabase tool)
i want to this project , but iam dont have money
Is this feature possible without using spatie/laravel-permission?
Yes it is. But you will have to build everything yourself :)