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

# 控制器

> 說明 Laravel 控制器的建立方式與其與路由的整合。

## 控制器是什麼

不需要把所有請求處理邏輯以閉包定義在路由檔案中，你可以透過「控制器」類別來整理這些行為。
控制器能將相關的請求處理邏輯統整到單一類別。

例如，`UserController` 類別可以處理與使用者有關的所有請求（顯示、建立、更新、刪除等）。
預設情況下，控制器會存放在 `app/Http/Controllers` 目錄。

## 建立控制器

要快速產生新的控制器，可使用 `make:controller` Artisan 指令。
應用程式的所有控制器預設都會存放在 `app/Http/Controllers` 目錄。

```shell theme={null}
php artisan make:controller UserController
```

## 基本控制器

控制器類別可有數個 public 方法來回應收到的 HTTP 請求。

```php theme={null}
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\View\View;

class UserController extends Controller
{
    /**
     * 顯示指定使用者的個人資料
     */
    public function show(string $id): View
    {
        return view('user.profile', [
            'user' => User::findOrFail($id)
        ]);
    }
}
```

建立好控制器類別與方法後，就可以像這樣為控制器方法定義路由：

```php theme={null}
use App\Http\Controllers\UserController;

Route::get('/user/{id}', [UserController::class, 'show']);
```

當收到的請求符合指定的路由 URI 時，`App\Http\Controllers\UserController` 類別的 `show` 方法會被呼叫，並將路由參數傳入方法中。

<Info>
  控制器不必繼承基底類別，但若有共通的方法可以繼承一個基底控制器類別，也會很方便。
</Info>

## 單一動作控制器

若某個控制器動作特別複雜，讓整個控制器類別專注處理該單一動作有時會很有幫助。
可透過在控制器內定義單一的 `__invoke` 方法來實現。

```php theme={null}
<?php

namespace App\Http\Controllers;

class ProvisionServer extends Controller
{
    /**
     * 佈建新的 Web 伺服器
     */
    public function __invoke()
    {
        // ...
    }
}
```

註冊單一動作控制器的路由時，不需要指定方法。
只需將控制器名稱傳給路由器：

```php theme={null}
use App\Http\Controllers\ProvisionServer;

Route::post('/server', ProvisionServer::class);
```

可透過 `make:controller` Artisan 指令的 `--invokable` 選項產生可呼叫的控制器。

```shell theme={null}
php artisan make:controller ProvisionServer --invokable
```

## 資源控制器

如果將應用程式中的每個 Eloquent 模型視為一種「資源」，那麼對每個資源執行相同的操作組合（CRUD）是很常見的做法。
Laravel 的資源路由能以一行程式，將典型的建立 / 讀取 / 更新 / 刪除（CRUD）路由指派給控制器。

可透過 `make:controller` Artisan 指令的 `--resource` 選項，快速建立能處理這些動作的控制器。

```shell theme={null}
php artisan make:controller PhotoController --resource
```

此指令會在 `app/Http/Controllers/PhotoController.php` 產生控制器。
控制器中會包含每個資源操作對應方法的雛形（stub）。
接著註冊指向該控制器的資源路由。

```php theme={null}
use App\Http\Controllers\PhotoController;

Route::resource('photos', PhotoController::class);
```

這一行的路由宣告會建立多條路由，用來處理資源的各種動作。
產生的控制器也已包含這些動作對應方法的雛形。

| HTTP 方法   | URI                    | 動作      | 路由名稱           |
| --------- | ---------------------- | ------- | -------------- |
| GET       | `/photos`              | index   | photos.index   |
| GET       | `/photos/create`       | create  | photos.create  |
| POST      | `/photos`              | store   | photos.store   |
| GET       | `/photos/{photo}`      | show    | photos.show    |
| GET       | `/photos/{photo}/edit` | edit    | photos.edit    |
| PUT/PATCH | `/photos/{photo}`      | update  | photos.update  |
| DELETE    | `/photos/{photo}`      | destroy | photos.destroy |

<Info>
  執行 `php artisan route:list` 指令可以快速確認應用程式路由的總覽。
</Info>

### 部分資源路由

在宣告資源路由時，可以指定控制器要處理的動作子集。

```php theme={null}
use App\Http\Controllers\PhotoController;

Route::resource('photos', PhotoController::class)->only([
    'index', 'show'
]);

Route::resource('photos', PhotoController::class)->except([
    'create', 'store', 'update', 'destroy'
]);
```

### API 資源路由

若要為 API 宣告資源路由，通常會希望排除顯示 HTML 樣板的 `create` 或 `edit` 路由。
使用 `apiResource` 方法可自動排除這 2 條路由。

```php theme={null}
use App\Http\Controllers\PhotoController;

Route::apiResource('photos', PhotoController::class);
```

## 依賴注入

### 建構子注入

Laravel 的服務容器負責解析所有 Laravel 控制器的相依。
因此，你可以在控制器的建構子中以型別提示指定所需的相依。
宣告的相依會自動被解析並注入到控制器實例中。

```php theme={null}
<?php

namespace App\Http\Controllers;

use App\Repositories\UserRepository;

class UserController extends Controller
{
    /**
     * 建立新的控制器實例
     */
    public function __construct(
        protected UserRepository $users,
    ) {}
}
```

### 方法注入

除了建構子注入，控制器方法的相依也能以型別提示指定。
方法注入常見的用途，就是將 `Illuminate\Http\Request` 實例注入到控制器方法中。

```php theme={null}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * 儲存新的使用者
     */
    public function store(Request $request): RedirectResponse
    {
        $name = $request->name;

        // 儲存使用者的處理...

        return redirect('/users');
    }
}
```

若控制器方法同時要接收路由參數，請將路由參數列在其他相依之後。
例如以下路由：

```php theme={null}
use App\Http\Controllers\UserController;

Route::put('/user/{id}', [UserController::class, 'update']);
```

可如下定義控制器方法，同時型別提示 `Illuminate\Http\Request` 並取得 `id` 參數。

```php theme={null}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * 更新指定的使用者
     */
    public function update(Request $request, string $id): RedirectResponse
    {
        // 更新使用者的處理...

        return redirect('/users');
    }
}
```

<Tip>
  善用依賴注入，可以在測試時更容易置入 mock，提升程式的可測試性。
</Tip>

## 下一步

<Card title="路由" icon="route" href="/zh-TW/routing">
  回顧路由的定義方式與與控制器的整合。
</Card>


## Related topics

- [HTTP Request](/zh-TW/requests.md)
- [Eloquent API 資源](/zh-TW/eloquent-resources.md)
- [回應（Response）](/zh-TW/responses.md)
- [Laravel 11 以後的新應用程式結構 FAQ](/zh-TW/advanced/app-structure-faq.md)
- [Laravel 13 新功能彙總](/zh-TW/blog/laravel-13-new-features.md)
