In this lesson, we will create a Middleware to check user roles and add this Middleware to two Route groups we created earlier for students and teachers.
First, we need to create the Middleware.
php artisan make:middleware RoleMiddleware
This Middleware will accept a role ID as a parameter, and the Middleware itself will use abort_if
Laravel helper.
app/Http/Middleware/RoleMiddleware.php:
use Closure;use Illuminate\Http\Request;use Symfony\Component\HttpFoundation\Response; class RoleMiddleware{ public function handle(Request $request, Closure $next, int $roleId): Response { abort_if(auth()->user()->role_id !== $roleId, Response::HTTP_FORBIDDEN); return $next($request); }}
Next, we need to register an alias for the Middleware.
bootstrap/app.php:
return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { $middleware->alias([ 'role' => \App\Http\Middleware\RoleMiddleware::class, ]); }) ->withExceptions(function (Exceptions $exceptions) { // })->create();
Now, we can use this Middleware to provide the role ID. The syntax for adding a parameter to the Middleware is {role_name}:{parameter}
.
routes/web.php:
Route::get('/', function () { return view('welcome');}); Route::middleware(['auth', 'verified'])->group(function () { Route::prefix('student') ->middleware('role:1') ->name('student.') ->group(function () { Route::get('timetable', [\App\Http\Controllers\Student\TimetableController::class, 'index']) ->name('timetable'); }); Route::prefix('teacher') ->middleware('role:2') ->name('teacher.') ->group(function () { Route::get('timetable', [\App\Http\Controllers\Teacher\TimetableController::class, 'index']) ->name('timetable'); });}); Route::middleware('auth')->group(function () { Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');}); require __DIR__ . '/auth.php';
If I am logged in with the student user and try to access the teacher URL, I get a forbidden message.
I agree it's not pretty to have a parameter like this: role:1
. It doesn't really say what that "one" is: a student or a teacher? I thought about it. I could do something like role:student
, which would be more readable.
But then, in the Middleware, there would be an extra database query to determine the relationship between role names instead of getting that from the already existing user model. So, that's your choice. I've chosen the pass role ID as a parameter. It is pretty clear that it is a student because of other names in the same route group.
However, roles and permissions may be much more complex in your case. You may have a package like spatie/laravel-permission
. You may have a many-to-many relationship between users and roles.
In your case, this Middleware may be different, and the parameters may be different. However, the principle should stay the same. So this is exactly what I want to show in this mini-course: the principle, with a simple example.