> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Controller PHP Attributes

> Use #[Middleware], #[WithoutMiddleware], and #[Authorize] attributes added in Laravel 13 to declaratively configure middleware and authorization on controllers.

## Overview

Laravel 13 lets you assign middleware and authorization checks to controllers using PHP attributes. Instead of implementing the `HasMiddleware` interface or adding `can` middleware to routes, you simply annotate the class or method.

```php theme={null}
use Illuminate\Routing\Attributes\Controllers\Authorize;
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class PostController
{
    #[Middleware('subscribed')]
    #[Authorize('create', Post::class)]
    public function store(Request $request): Response
    {
        // ...
    }
}
```

<Tip>
  All controller attributes live in the `Illuminate\Routing\Attributes\Controllers` namespace.
</Tip>

## `#[Middleware]` — Assigning Middleware

### Applying to a Class

A class-level `#[Middleware]` attribute applies the middleware to every action in the controller.

```php theme={null}
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class UserController
{
    public function index(): View { /* ... */ }
    public function show(User $user): View { /* ... */ }
    public function store(Request $request): Response { /* ... */ }
}
```

Stack multiple attributes to apply several middleware:

```php theme={null}
#[Middleware('auth')]
#[Middleware('verified')]
class ProfileController
{
    // ...
}
```

### Applying to a Method

Method-level middleware is merged with any class-level middleware.

```php theme={null}
#[Middleware('auth')]
class UserController
{
    // Only 'auth' applied
    public function index(): View { /* ... */ }

    // Both 'auth' and 'subscribed' applied
    #[Middleware('subscribed')]
    public function create(): View { /* ... */ }
}
```

### `only` / `except` Constraints

Use `only` or `except` on a class-level attribute to limit which methods it targets.

```php theme={null}
#[Middleware('auth')]
#[Middleware('subscribed', only: ['create', 'store', 'edit', 'update'])]
class ArticleController
{
    // auth only
    public function index(): View { /* ... */ }
    public function show(Article $article): View { /* ... */ }

    // auth + subscribed
    public function create(): View { /* ... */ }
    public function store(Request $request): Response { /* ... */ }
    public function edit(Article $article): View { /* ... */ }
    public function update(Request $request, Article $article): Response { /* ... */ }

    // auth only
    public function destroy(Article $article): Response { /* ... */ }
}
```

### Closure Middleware

Attributes also accept closures for inline middleware logic.

```php theme={null}
use Closure;
use Illuminate\Http\Request;
use Illuminate\Routing\Attributes\Controllers\Middleware;

class ReportController
{
    #[Middleware(static function (Request $request, Closure $next) {
        if (! $request->user()->hasRole('analyst')) {
            abort(403);
        }

        return $next($request);
    })]
    public function generate(): Response
    {
        // ...
    }
}
```

### Comparison with the `middleware()` Method

```php theme={null}
// Old approach — implement HasMiddleware
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;

class UserController implements HasMiddleware
{
    public static function middleware(): array
    {
        return [
            'auth',
            new Middleware('log', only: ['index']),
            new Middleware('subscribed', except: ['store']),
        ];
    }
}

// Attribute approach — no interface required
#[Middleware('auth')]
#[Middleware('log', only: ['index'])]
#[Middleware('subscribed', except: ['store'])]
class UserController
{
}
```

<Info>
  When using attributes you do not need to implement `HasMiddleware`. Avoid mixing the `middleware()` method with attributes on the same controller to prevent unexpected behavior.
</Info>

## `#[WithoutMiddleware]` — Excluding Middleware

Remove middleware that was applied at the class level from specific methods or the entire class.

### Applying to a Method

```php theme={null}
use App\Http\Middleware\EnsureTokenIsValid;
use Illuminate\Routing\Attributes\Controllers\Middleware;
use Illuminate\Routing\Attributes\Controllers\WithoutMiddleware;

#[Middleware('auth')]
#[Middleware(EnsureTokenIsValid::class)]
class ApiController
{
    // Both 'auth' and EnsureTokenIsValid applied
    public function show(Resource $resource): JsonResponse { /* ... */ }

    // Only 'auth' applied — EnsureTokenIsValid removed
    #[WithoutMiddleware(EnsureTokenIsValid::class)]
    public function index(): JsonResponse { /* ... */ }
}
```

### Class-Level Exclusion with `only` / `except`

```php theme={null}
#[Middleware('auth')]
#[WithoutMiddleware('subscribed', except: ['index'])]
class AdminController
{
    // 'subscribed' applies (excluded from the exclusion via except)
    public function index(): View { /* ... */ }

    // 'subscribed' is removed
    public function dashboard(): View { /* ... */ }
    public function settings(): View { /* ... */ }
}
```

<Warning>
  `#[WithoutMiddleware]` only removes **route middleware**. It cannot remove global middleware registered in `app/Http/Kernel.php`.
</Warning>

## `#[Authorize]` — Policy Authorization

A convenient shorthand for the `can` middleware. Declare policy checks directly on controller methods.

### Basic Usage

```php theme={null}
use App\Models\Post;
use Illuminate\Routing\Attributes\Controllers\Authorize;

class PostController
{
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

    #[Authorize('view', 'post')]
    public function show(Post $post): View { /* ... */ }

    #[Authorize('create', Post::class)]
    public function create(): View { /* ... */ }

    #[Authorize('create', Post::class)]
    public function store(Request $request): Response { /* ... */ }

    #[Authorize('update', 'post')]
    public function edit(Post $post): View { /* ... */ }

    #[Authorize('update', 'post')]
    public function update(Request $request, Post $post): Response { /* ... */ }

    #[Authorize('delete', 'post')]
    public function destroy(Post $post): Response { /* ... */ }
}
```

### The Second Argument

| Value                      | Meaning                                                                      |
| -------------------------- | ---------------------------------------------------------------------------- |
| `Post::class`              | Model class (for abilities that don't need a model instance, e.g. `viewAny`) |
| `'post'`                   | Route parameter name — the bound model instance is passed to the policy      |
| `[Comment::class, 'post']` | Multiple arguments passed as an array to the policy method                   |

```php theme={null}
use App\Models\Comment;
use App\Models\Post;
use Illuminate\Routing\Attributes\Controllers\Authorize;

class CommentController
{
    #[Authorize('create', [Comment::class, 'post'])]
    public function store(Post $post, Request $request): Response
    {
        // ...
    }

    #[Authorize('delete', 'comment')]
    public function destroy(Comment $comment): Response
    {
        // ...
    }
}
```

### Comparison with Route Middleware

```php theme={null}
// Old approach — middleware in route file
Route::get('/posts', [PostController::class, 'index'])->middleware('can:viewAny,App\Models\Post');
Route::put('/posts/{post}', [PostController::class, 'update'])->middleware('can:update,post');

// Attribute approach — authorization lives with the controller
class PostController
{
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

    #[Authorize('update', 'post')]
    public function update(Request $request, Post $post): Response { /* ... */ }
}
```

## Practical Example: Full Resource Controller

```php theme={null}
use App\Models\Article;
use Illuminate\Routing\Attributes\Controllers\Authorize;
use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class ArticleController
{
    #[Authorize('viewAny', Article::class)]
    public function index(): View
    {
        return view('articles.index', ['articles' => Article::paginate()]);
    }

    #[Authorize('view', 'article')]
    public function show(Article $article): View
    {
        return view('articles.show', compact('article'));
    }

    #[Authorize('create', Article::class)]
    public function create(): View
    {
        return view('articles.create');
    }

    #[Authorize('create', Article::class)]
    public function store(StoreArticleRequest $request): RedirectResponse
    {
        $article = Article::create($request->validated());

        return redirect()->route('articles.show', $article);
    }

    #[Authorize('update', 'article')]
    public function edit(Article $article): View
    {
        return view('articles.edit', compact('article'));
    }

    #[Authorize('update', 'article')]
    public function update(UpdateArticleRequest $request, Article $article): RedirectResponse
    {
        $article->update($request->validated());

        return redirect()->route('articles.show', $article);
    }

    #[Authorize('delete', 'article')]
    public function destroy(Article $article): RedirectResponse
    {
        $article->delete();

        return redirect()->route('articles.index');
    }
}
```

## When to Use Attributes vs. Other Approaches

| Situation                               | Recommended Approach             |
| --------------------------------------- | -------------------------------- |
| New controller on Laravel 13            | Use attributes                   |
| Existing controller with `middleware()` | Migrate to attributes gradually  |
| Dynamic middleware configuration        | Keep using `middleware()` method |
| Centralized route middleware management | Keep using `Route::middleware()` |

## Next Steps

<Columns cols={2}>
  <Card title="PHP Attributes (Queues & Eloquent)" icon="code" href="/en/advanced/php-attributes">
    PHP attributes for queue jobs and Eloquent models.
  </Card>

  <Card title="Controllers" icon="layer-group" href="/en/controllers">
    Learn the basics of Laravel controllers.
  </Card>
</Columns>


## Related topics

- [Laravel 13 new features overview](/en/blog/laravel-13-new-features.md)
- [PHP Attributes](/en/advanced/php-attributes.md)
- [March 2026 Laravel updates](/en/blog/changelog/202603.md)
- [PHP Reflection API](/en/advanced/php-reflection.md)
- [Eloquent Bootable Traits](/en/advanced/eloquent-bootable-traits.md)
