Controller middleware — in constructor vs in route
Concept
Controller middleware applies middleware to all or specific methods of a controller. This is an alternative to applying middleware in the route definition — it co-locates middleware with the controller it affects.
Constructor middleware (Laravel ≤ 10): In the controller's __construct(), call $this->middleware('auth'). Chain .only(['store', 'update', 'destroy']) or .except(['index', 'show']) to restrict which methods it applies to.
Laravel 11 method: Laravel 11 introduced static middleware() method on controllers as the new pattern. Return an array from the static middleware() method. The array maps middleware to methods.
Route definition middleware: Route::get('/users', [UserController::class, 'index'])->middleware('auth') — applied in the route file. Easier to see in route:list output; keeps all middleware in one place.
Which approach to choose:
- Route definition (preferred by many teams): All middleware visible in route files.
php artisan route:listshows everything. Consistent location. - Controller constructor: Co-located with the controller. Good when the controller "knows" it needs specific middleware (e.g., a fully authenticated resource controller).
Middleware groups in controller: You can reference any named middleware, middleware groups, or custom middleware by alias. $this->middleware(['auth', 'verified'])->only(['create', 'store']).
Code Example
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
// Laravel ≤ 10 — constructor middleware
class ArticleController extends Controller
{
public function __construct()
{
// Apply auth to everything except index/show (public reading allowed)
$this->middleware('auth')->except(['index', 'show']);
// Apply verified only to create/store
$this->middleware('verified')->only(['create', 'store']);
// Apply custom middleware to all methods
$this->middleware('subscription:pro')->only(['advancedSearch']);
// Chain multiple middleware
$this->middleware(['auth', 'can:publish-articles'])->only(['publish']);
}
public function index() { /* public — no auth required */ }
public function show(Article $article) { /* public */ }
public function create() { /* requires auth + verified */ }
public function store(Request $request) { /* requires auth + verified */ }
public function publish(Article $article) { /* requires auth + can:publish-articles */ }
}
// Laravel 11 — static middleware() method
class ArticleController extends Controller
{
public static function middleware(): array
{
return [
new \Illuminate\Routing\Controllers\Middleware('auth', except: ['index', 'show']),
new \Illuminate\Routing\Controllers\Middleware('verified', only: ['create', 'store']),
];
}
}
// Route definition approach (explicit, visible in route:list)
Route::middleware('auth')->group(function() {
Route::post('/articles', [ArticleController::class, 'store']);
Route::put('/articles/{article}', [ArticleController::class, 'update']);
Route::delete('/articles/{article}', [ArticleController::class, 'destroy']);
});
// No auth required for:
Route::get('/articles', [ArticleController::class, 'index']);
Route::get('/articles/{article}', [ArticleController::class, 'show']);