Idea
Add a custom rule that flags $this->middleware(...) calls inside controller constructors. The pattern is deprecated in Laravel 11+: middleware should be declared in route definitions (e.g. Route::middleware([...])->group(...)) or via the HasMiddleware contract, not in the controller body.
Why
- Laravel 11's slim skeleton drops
Illuminate\Routing\Controller's middleware() method by default. Code using $this->middleware(...) either silently no-ops, errors at runtime, or relies on legacy compatibility imports.
- Mixing middleware in controllers fragments the request pipeline across two locations, making it harder to audit which middleware applies to which route.
- A static check catches stale controller-side middleware that survives a slim-skeleton migration.
Suggested behavior
- Fire on
$this->middleware(...) (or $this->middleware(...)->only(...), ->except(...)) inside any class extending Illuminate\Routing\Controller or implementing a controller marker.
- Emit a new plugin issue (e.g.
LaravelControllerMiddleware) suggesting the route-side equivalent or HasMiddleware.
- Configurable severity via plugin config; default to a soft warning so legacy projects can opt in.
Prior art
mago ships an equivalent lint rule:
Middleware In Routes (middleware-in-routes, category: best_practices):
"This rule warns against applying middlewares in controllers. Middlewares should be applied in the routes file, not in the controller."
Source: crates/linter/src/rule/best_practices/middleware_in_routes.rs.
mago's rule is purely AST-driven (no type info needed). Our implementation can be similar — pattern match on MethodCall to $this->middleware inside controller class bodies.
Out of scope
- Auto-fix to relocate the middleware call into a route file (would need to know which routes reference the controller — non-trivial).
- Detecting middleware grouped via the
HasMiddleware contract (that's the recommended replacement, should not be flagged).
Idea
Add a custom rule that flags
$this->middleware(...)calls inside controller constructors. The pattern is deprecated in Laravel 11+: middleware should be declared in route definitions (e.g.Route::middleware([...])->group(...)) or via theHasMiddlewarecontract, not in the controller body.Why
Illuminate\Routing\Controller'smiddleware()method by default. Code using$this->middleware(...)either silently no-ops, errors at runtime, or relies on legacy compatibility imports.Suggested behavior
$this->middleware(...)(or$this->middleware(...)->only(...),->except(...)) inside any class extendingIlluminate\Routing\Controlleror implementing a controller marker.LaravelControllerMiddleware) suggesting the route-side equivalent orHasMiddleware.Prior art
mago ships an equivalent lint rule:
Source:
crates/linter/src/rule/best_practices/middleware_in_routes.rs.mago's rule is purely AST-driven (no type info needed). Our implementation can be similar — pattern match on
MethodCallto$this->middlewareinside controller class bodies.Out of scope
HasMiddlewarecontract (that's the recommended replacement, should not be flagged).