0

Controller middleware — in constructor vs in route

Intermediate5 min read·lv-08-003

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:list shows 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
<?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']);