Skip to content

New rule: detect $this->middleware() inside controller constructors #962

@alies-dev

Description

@alies-dev

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).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions