> ## 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

> 解說如何使用 Laravel 13 新增的 #[Middleware]、#[WithoutMiddleware]、#[Authorize] attributes，以宣告式方式為 Controller 設定中介層與授權。

## 概觀

Laravel 13 中，可透過 PHP Attributes 宣告 Controller 的中介層指派與授權檢查。取代原本的 `middleware()` 方法及 `can` 中介層，只需在類別或方法上直接加入 attribute 即可完成設定。

```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>
  Controller 的 attributes 皆位於 `Illuminate\Routing\Attributes\Controllers` 命名空間下。
</Tip>

## `#[Middleware]` — 指派中介層

### 套用於類別

在類別層級加上 `#[Middleware]`，該中介層將套用至該 Controller 的所有 action。

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

要指派多個中介層時，重複標註 attribute 即可。

```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` 限定

在類別層級的 attribute 中指定 `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 { /* ... */ }
}
```

### Closure 中介層

Attribute 也可接受 closure。適合想以行內方式撰寫處理的場合。

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

// 使用 attribute 的寫法
#[Middleware('auth')]
#[Middleware('log', only: ['index'])]
#[Middleware('subscribed', except: ['store'])]
class UserController
{
    // 不需實作 HasMiddleware
}
```

<Info>
  使用 attribute 時無需實作 `HasMiddleware` 介面。但將 `middleware()` 方法與 attribute 混用可能導致預期外的行為，建議統一擇一使用。
</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]`，則包含子類別在內的所有 action 都會排除該中介層。可以 `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]` — 透過 Policy 進行授權

作為 `can` 中介層的語法糖，可以 attribute 宣告透過 Policy 的授權檢查。

### 基本用法

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

class PostController
{
    // 檢查 'viewAny' 能力（Post Policy 的 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 { /* ... */ }
}
```

### Policy 的引數

第 2 個引數可傳入以下形式。

| 傳入值                        | 說明                              |
| -------------------------- | ------------------------------- |
| `Post::class`              | 模型類別（適用於 `viewAny` 等不需要模型實例的情況） |
| `'post'`                   | 路由參數名稱（會傳入被模型繫結後的實例）            |
| `[Comment::class, 'post']` | 多個引數（以陣列形式傳入 Policy 方法）         |

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

class CommentController
{
    // 將 $post（路由參數）與 Comment class 傳入 Policy
    #[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 Facade）
Route::get('/posts', [PostController::class, 'index'])->middleware('can:viewAny,App\Models\Post');
Route::put('/posts/{post}', [PostController::class, 'update'])->middleware('can:update,post');

// 使用 attribute 的寫法
class PostController
{
    #[Authorize('viewAny', Post::class)]
    public function index(): View { /* ... */ }

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

不再需要在路由檔案中撰寫中介層，授權邏輯集中於 Controller。

## 實務範例

### 套用於 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');
    }
}
```

### 套用於 API Controller

```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
    {
        // 僅限已驗證使用者
    }
}
```

## Attribute 的處理順序

當有多個 attribute 存在時，處理順序如下。

```mermaid theme={null}
flowchart LR
    A["請求"] --> B["類別層級的<br>#[Middleware]"]
    B --> C["方法層級的<br>#[Middleware]"]
    C --> D["#[WithoutMiddleware]<br>的排除處理"]
    D --> E["#[Authorize]<br>的授權檢查"]
    E --> F["執行 Controller<br>方法"]
```

<Info>
  `#[Authorize]` 內部作為 `can` 中介層運作，因此於相同的 Pipeline 中處理。
</Info>

## 總結：該使用哪一種？

| 情境                                 | 建議做法                       |
| ---------------------------------- | -------------------------- |
| 新的 Controller（Laravel 13）          | 使用 attribute               |
| 既有具備 `middleware()` 方法的 Controller | 逐步遷移至 attribute            |
| 需要動態的中介層設定                         | 繼續使用 `middleware()` 方法     |
| 想在路由檔案集中管理                         | 繼續使用 `Route::middleware()` |

## 下一步

<Columns cols={2}>
  <Card title="PHP Attributes（Queue、Eloquent）" icon="code" href="/zh-TW/advanced/php-attributes">
    解說可用於 Queue Job 及 Eloquent 模型的 PHP Attributes。
  </Card>

  <Card title="中階：Controller" icon="layer-group" href="/zh-TW/controllers">
    學習 Laravel Controller 的基本用法。
  </Card>
</Columns>


## Related topics

- [PHP Attributes](/zh-TW/advanced/php-attributes.md)
- [Laravel 11 以後的新應用程式結構 FAQ](/zh-TW/advanced/app-structure-faq.md)
- [Eloquent 的 Scope](/zh-TW/advanced/eloquent-scopes.md)
- [PHP Reflection API](/zh-TW/advanced/php-reflection.md)
- [Eloquent Observer 與模型事件](/zh-TW/advanced/eloquent-observers.md)
