Courses

[FREE] Laravel 12 For Beginners: Your First Project

Admin User, Route Groups, Middleware

Summary of this lesson:
- Add admin user column to database and create custom Admin Middleware
- Implement Route Groups to protect authenticated routes
- Restrict category management to admin users only
- Use Laravel Middleware to check user authorization before accessing routes

Now, let's discuss Middleware and authorization. We will create a special users.is_admin column in the DB and restrict non-admin users from access to manage categories.


Route Groups and Middleware

We have already seen the auth Middleware, which allows only logged-in users to access Routes. The auth is one of the Middlewares offered by the Laravel framework.

Middleware runs some checks before Routes, and if it returns false, it shows errors or redirects to an error page. For example, if someone wants to visit the /dashboard URL protected with auth Middleware, they will automatically be redirected to the login page.

Now, look at our current Routes file.

routes/web.php:

use App\Http\Controllers\CategoryController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
 
Route::get('/', function () {
return view('welcome');
});
 
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
 
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');
});
 
Route::resource('categories', CategoryController::class);
 
require __DIR__.'/auth.php';

I see two problems here:

  1. Middleware auth is used for both the Dashboard and the Profile group: can we somehow combine them?
  2. The Categories resource doesn't use auth, so it's available to every public user without logging in.

In the routes/web.php, we already have one Route group for the profile links. We can move both Dashboard and Categories routes to that group.

The syntax to have grouped Routes is to use what you need on a Route facade like middleware, prefix, etc., and then have a group method with a closure and add all the Routes in the closure.

routes/web.php:

Route::get('/', function () {
return view('welcome');
});
 
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
 
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');
 
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['verified'])->name('dashboard');
 
Route::resource('categories', CategoryController::class);
});
 
require __DIR__.'/auth.php';
 
Route::resource('categories', CategoryController::class);

If you try to access any of these routes as a guest, you should be redirected to the login page.

As you can see, we moved the Dashboard inside the auth Middleware group. Still, we can additionally assign more Middleware checks like verified (is user's email verified) to any Route within a group.

Here's the GitHub commit for this change.


Admin User: DB Structure

Let's add a new column to the users table to define whether the user is an admin.

php artisan make:migration add_is_admin_to_users_table

Here's what's inside our Migration:

public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('is_admin')->default(false);
});
}

If we run php artisan migrate, we will have this column:


Categories: Only for Admin Users

Now, let's create our custom Middleware to check if a user is an admin. Middleware can be created using an Artisan command.

php artisan make:middleware IsAdminMiddleware

Middlewares are created in the app/Http/Middleware folder. The default structure of the Middleware is the handle(), which returns the next request. Before the return, you can add conditions your application needs and abort the request if needed.

In our created Middleware, if the is_admin field in the User table is false, we must abort with the "403 Forbidden" status. Here's the syntax.

app/Http/Middleware/IsAdminMiddleware.php:

namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class IsAdminMiddleware
{
public function handle(Request $request, Closure $next): Response
{
if (! auth()->check() || ! auth()->user()->is_admin) {
abort(403);
}
 
return $next($request);
}
}

The currently authenticated user can be obtained using Laravel helpers auth()->user() and then call the field liks is_admin.

Also, there's auth()->check(), which returns true if the user is logged in.

Next, we can use the Middleware in our Routes by providing the class name directly.

routes/web.php:

use App\Http\Middleware\IsAdminMiddleware;
 
// ...
 
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
 
// ...
 
Route::resource('categories', CategoryController::class)
->middleware(IsAdminMiddleware::class);
});

If you try to access categories Routes with a user who isn't an admin, you will see a forbidden error message:

So, this is how you use Route Groups and Middlewares in Laravel to restrict access to some routes.

Here's the GitHub commit for this lesson.


In the next lesson, we will practice to create another CRUD, managing Posts.

No comments or questions yet...