Laravel Users with Multiple Roles: Switching Between Roles

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:

  1. Information about the current role
  2. 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

avatar

What database design tool did you use?

avatar

This was an existing database view from DataGrip (jetbrains darabase tool)

avatar
Muhammad Dikky Purwanto

i want to this project , but iam dont have money

avatar

Is this feature possible without using spatie/laravel-permission?

avatar

Yes it is. But you will have to build everything yourself :)

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 68 courses (1188 lessons, total 43 h 18 min)
  • 90 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent New Courses