Courses

Roles and Permissions in Laravel 12

Patient Registration: Choose Team/Clinic

Summary of this lesson:
- Implementing user listing with role filtering
- Creating role-based user creation system
- Setting up user management policies
- Writing tests for user management features

When a user tries to register as a Patient, they can choose a team/clinic.

But we don't want users to register for the Master Team.

app/Http/Controllers/Auth/RegisteredUserController.php:

use App\Models\Team;
use Illuminate\View\View;
 
class RegisteredUserController extends Controller
{
public function create(): View
{
$teams = Team::where('name', '!=', 'Master Admin Team')->pluck('name', 'id');
 
return view('auth.register', compact('teams'));
}
 
// ...
}

resources/views/auth/register.blade.php:

// ...
 
<!-- Email Address -->
<div class="mt-4">
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autocomplete="username" />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
 
<!-- Team/Clinic -->
<div class="mt-4">
<x-input-label for="team_id" :value="__('Team')" />
<select name="team_id" id="team_id" class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm">
<option>-- SELECT TEAM --</option>
@foreach($teams as $id => $name)
<option value="{{ $id }}">{{ $name }}</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('team_id')" class="mt-2" />
</div>
 
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
 
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="new-password" />
 
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
 
// ...

When a user is created, we must set the current_team_id, then set the team ID using the setPermissionsTeamId() helper from the Spatie package, and assign a role.

app/Http/Controllers/Auth/RegisteredUserController.php:

use App\Enums\Role;
use App\Models\Team;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
use Illuminate\View\View;
 
class RegisteredUserController extends Controller
{
// ...
public function store(Request $request): RedirectResponse
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
'team_id' => ['required', 'exists:teams,id'],
]);
 
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'current_team_id' => $request->team_id,
]);
 
event(new Registered($user));
 
Auth::login($user);
 
setPermissionsTeamId($request->team_id);
 
$user->assignRole(Role::Patient);
 
return redirect(route('dashboard', absolute: false));
}
}

When using teams with the Spatie package, they recommend using the Middleware to the user's current team ID.

php artisan make:middleware TeamsPermissionMiddleware

app/Http/Middleware/TeamsPermissionMiddleware.php:

use Closure;
use Illuminate\Http\Request;
use Spatie\Permission\PermissionRegistrar;
 
class TeamsPermissionMiddleware
{
public function handle(Request $request, Closure $next)
{
if (! empty($user = auth()->user()) && ! empty($user->current_team_id)) {
app(PermissionRegistrar::class)->setPermissionsTeamId($user->current_team_id);
}
 
return $next($request);
}
}

bootstrap/app.php:

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
 
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
\App\Http\Middleware\TeamsPermissionMiddleware::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
Previous: Clinic Owner: Switching Between Teams
avatar

amazing lesson!

Note in this case the Master Admin Team is removed from the patient registration form drop down but a bad actor could still pass this to the controller and create it. you'd want to double check this at the creation step too before passing it to DB.

avatar
You can use Markdown
avatar
You can use Markdown