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

# コントローラーの PHPアトリビュート

> Laravel 13 で追加された #[Middleware]、#[WithoutMiddleware]、#[Authorize] アトリビュートを使い、コントローラーへのミドルウェアや認可の設定を宣言的に記述する方法を解説します。

## 概要

Laravel 13 では、コントローラーへのミドルウェア割り当てと認可チェックを PHP アトリビュートで宣言できるようになりました。従来の `middleware()` メソッドや `can` ミドルウェアの代わりに、クラスやメソッドに直接アトリビュートを付与するだけで設定が完結します。

```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>
  コントローラーアトリビュートはすべて `Illuminate\Routing\Attributes\Controllers` 名前空間にあります。
</Tip>

## `#[Middleware]` — ミドルウェアを割り当てる

### クラスへの適用

クラスレベルに `#[Middleware]` を付与すると、そのコントローラーの全アクションにミドルウェアが適用されます。

```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 { /* ... */ }
}
```

複数のミドルウェアを付与する場合は、アトリビュートを繰り返します。

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

### メソッドへの適用

メソッドレベルに付与したミドルウェアは、クラスレベルのミドルウェアとマージされます。

```php theme={null}
#[Middleware('auth')]
class UserController
{
    // 全員に auth のみ
    public function index(): View { /* ... */ }

    // auth + subscribed の両方が適用される
    #[Middleware('subscribed')]
    public function create(): View { /* ... */ }
}
```

### `only` / `except` による絞り込み

クラスレベルのアトリビュートに `only` または `except` を指定すると、対象メソッドを絞り込めます。

```php theme={null}
#[Middleware('auth')]
#[Middleware('subscribed', only: ['create', 'store', 'edit', 'update'])]
class ArticleController
{
    // auth のみ
    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 のみ（except で除外された場合と同等）
    public function destroy(Article $article): Response { /* ... */ }
}
```

### クロージャーミドルウェア

アトリビュートにはクロージャーも使えます。インラインで処理を書きたい場合に便利です。

```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
    {
        // ...
    }
}
```

### 従来の `middleware()` メソッドとの比較

```php theme={null}
// 従来の書き方（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']),
        ];
    }
}

// アトリビュートを使った書き方
#[Middleware('auth')]
#[Middleware('log', only: ['index'])]
#[Middleware('subscribed', except: ['store'])]
class UserController
{
    // HasMiddleware の実装不要
}
```

<Info>
  アトリビュートを使う場合は `HasMiddleware` インターフェイスを実装する必要はありません。ただし、`middleware()` メソッドとアトリビュートを混在させると意図しない動作になる可能性があるため、どちらか一方に統一することを推奨します。
</Info>

## `#[WithoutMiddleware]` — ミドルウェアを除外する

クラスレベルで適用されたミドルウェアを、特定のメソッドまたはクラス全体から除外します。

### メソッドへの適用

```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
{
    // auth と EnsureTokenIsValid が適用される
    public function show(Resource $resource): JsonResponse { /* ... */ }

    // auth は適用されるが EnsureTokenIsValid は除外される
    #[WithoutMiddleware(EnsureTokenIsValid::class)]
    public function index(): JsonResponse { /* ... */ }
}
```

### クラスへの適用と `only` / `except`

`#[WithoutMiddleware]` をクラスレベルに付与すると、子クラスを含む全アクションからミドルウェアが除外されます。`only` / `except` で除外範囲を限定できます。

```php theme={null}
#[Middleware('auth')]
#[WithoutMiddleware('subscribed', except: ['index'])]
class AdminController
{
    // subscribed が適用される（except: ['index'] で除外されない）
    public function index(): View { /* ... */ }

    // subscribed は除外される
    public function dashboard(): View { /* ... */ }
    public function settings(): View { /* ... */ }
}
```

<Warning>
  `#[WithoutMiddleware]` は**ルートミドルウェア**にのみ有効です。`app/Http/Kernel.php` に登録されたグローバルミドルウェアは除外できません。
</Warning>

## `#[Authorize]` — ポリシーによる認可

`can` ミドルウェアの糖衣構文として、ポリシーによる認可チェックをアトリビュートで宣言できます。

### 基本的な使い方

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

class PostController
{
    // 'viewAny' アビリティをチェック（Post ポリシーの viewAny メソッド）
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

    // 'view' アビリティをチェック（ルートパラメーター 'post' を渡す）
    #[Authorize('view', 'post')]
    public function show(Post $post): View { /* ... */ }

    // 'create' アビリティをチェック
    #[Authorize('create', Post::class)]
    public function create(): View { /* ... */ }

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

    // 'update' アビリティをチェック（ルートパラメーターを渡す）
    #[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 { /* ... */ }
}
```

### ポリシーの引数

第2引数には以下を渡せます。

| 渡す値                        | 説明                                   |
| -------------------------- | ------------------------------------ |
| `Post::class`              | モデルクラス（`viewAny` などモデルインスタンス不要の場合）   |
| `'post'`                   | ルートパラメーター名（モデルバインディングされたインスタンスが渡される） |
| `[Comment::class, 'post']` | 複数の引数（ポリシーメソッドに配列で渡される）              |

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

class CommentController
{
    // $post（ルートパラメーター）と Comment クラスをポリシーに渡す
    #[Authorize('create', [Comment::class, 'post'])]
    public function store(Post $post, Request $request): Response
    {
        // ...
    }

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

### `can` ミドルウェアとの比較

```php theme={null}
// 従来の書き方（Route ファサード）
Route::get('/posts', [PostController::class, 'index'])->middleware('can:viewAny,App\Models\Post');
Route::put('/posts/{post}', [PostController::class, 'update'])->middleware('can:update,post');

// アトリビュートを使った書き方
class PostController
{
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

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

ルートファイルにミドルウェアを書く必要がなくなり、認可ロジックがコントローラーに集約されます。

## 実践的な例

### リソースコントローラーへの適用

```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');
    }
}
```

### API コントローラーへの適用

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

#[Middleware('auth:sanctum')]
#[Middleware('throttle:api')]
class ApiPostController
{
    // 一覧取得は認証なしで可能
    #[WithoutMiddleware('auth:sanctum')]
    public function index(): JsonResponse
    {
        return response()->json(Post::paginate());
    }

    public function store(Request $request): JsonResponse
    {
        // 認証済みユーザーのみ
    }
}
```

## アトリビュートの処理順序

複数のアトリビュートが付与されている場合、処理順序は以下のとおりです。

```mermaid theme={null}
flowchart LR
    A["リクエスト"] --> B["クラスレベルの<br>#[Middleware]"]
    B --> C["メソッドレベルの<br>#[Middleware]"]
    C --> D["#[WithoutMiddleware]<br>の除外処理"]
    D --> E["#[Authorize]<br>の認可チェック"]
    E --> F["コントローラー<br>メソッド実行"]
```

<Info>
  `#[Authorize]` は内部的に `can` ミドルウェアとして動作するため、`#[Middleware]` と同じパイプラインで処理されます。
</Info>

## まとめ：どちらを使うべきか

| 状況                                | 推奨アプローチ                     |
| --------------------------------- | --------------------------- |
| 新規コントローラー（Laravel 13）             | アトリビュートを使う                  |
| 既存の `middleware()` メソッドを持つコントローラー | 段階的にアトリビュートへ移行              |
| 動的なミドルウェア設定が必要                    | `middleware()` メソッドを継続使用    |
| ルートファイルで一元管理したい                   | `Route::middleware()` を継続使用 |

## 次のステップ

<Columns cols={2}>
  <Card title="PHPアトリビュート（キュー・Eloquent）" icon="code" href="/jp/advanced/php-attributes">
    キュージョブや Eloquent モデルで使える PHP アトリビュートの解説。
  </Card>

  <Card title="中級: コントローラー" icon="layer-group" href="/jp/controllers">
    Laravel コントローラーの基本的な使い方を学びます。
  </Card>
</Columns>


## Related topics

- [Laravel 13 新機能まとめ](/jp/blog/laravel-13-new-features.md)
- [コントローラー](/jp/controllers.md)
- [2026年3月 Laravel アップデート](/jp/blog/changelog/202603.md)
- [ビュー](/jp/views.md)
- [HTTPリクエスト](/jp/requests.md)
