# Laravel 的 HTTP 控制器
- [簡介](#introduction)
- [基礎控制器](#basic-controllers)
- [定義控制器](#defining-controllers)
- [控制器與命名空間](#controllers-and-namespaces)
- [單一操作控制器](#single-action-controllers)
- [控制器中間件](#controller-middleware)
- [資源控制器](#resource-controllers)
- [部分資源路由](#restful-partial-resource-routes)
- [命名資源路由](#restful-naming-resource-routes)
- [命名資源路由參數](#restful-naming-resource-route-parameters)
- [本地化資源 URI](#restful-localizing-resource-uris)
- [附加資源控制器](#restful-supplementing-resource-controllers)
- [依賴注入與控制器](#dependency-injection-and-controllers)
- [路由緩存](#route-caching)
<a name="introduction"></a>
## 簡介
除了在路由文件中以閉包的形式定義所有的請求處理邏輯外,你可能還想使用控制器類來組織此類操作。控制器能夠將相關的請求處理邏輯組成一個單獨的類。控制器被存放在 `app/Http/Controllers` 目錄下。
<a name="basic-controllers"></a>
## 基礎控制器
<a name="defining-controllers"></a>
### 定義控制器
下面是一個基礎控制器類的例子。需要注意的是,該控制器繼承了 Laravel 內置的基礎控制器類。該基礎控制器類提供了一些便捷的方法,比如 `middleware` 方法,該方法可以用來給控制器操作添加中間件:
<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* 展示給定用戶的信息。
*
* @param int $id
* @return Response
*/
public function show($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
你可以這樣定義一個指向該控制器操作的路由:
Route::get('user/{id}', 'UserController@show');
現在,當一個請求與此指定路由的 URI 匹配時, `UserController` 類的 `show` 方法就會被執行。當然,路由參數也會被傳遞至該方法。
> {tip} 控制器并不是 **一定** 要繼承基礎類。如果控制器沒有繼承基礎類,你將無法使用一些便捷的功能,比如 `middleware`, `validate` 和 `dispatch` 方法。
<a name="controllers-and-namespaces"></a>
### 控制器與命名空間
需要注意的是,當我們在定義控制器路由時,我們不需要指定完整的控制器命名空間。因為 `RouteServiceProvider` 會在一個包含命名空間的路由器組中加載路由文件,所以我們只需要指定類名中 `App\Http\Controllers` 命名空間之后的部分就可以了。
如果你選擇將你的控制器存放在 `App\Http\Controllers` 目錄下,只需要簡單地使用與 `App\Http\Controllers` 根命名空間相關的特定類名。因此,如果你的完整控制器類是 `App\Http\Controllers\Photos\AdminController` ,你應該用這種方式注冊指向該控制器的路由:
Route::get('foo', 'Photos\AdminController@method');
<a name="single-action-controllers"></a>
### 單一操作控制器
如果你想定義一個只處理單個操作的控制器,你可以在控制器中只放置一個 `__invoke` 方法:
<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class ShowProfile extends Controller
{
/**
* 展示給定用戶的信息。
*
* @param int $id
* @return Response
*/
public function __invoke($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
當給單一操作控制器注冊路由時,不需要指定方法:
Route::get('user/{id}', 'ShowProfile');
<a name="controller-middleware"></a>
## 控制器中間件
在路由文件中, [中間件](/docs/{{version}}/middleware) 可以被指定給控制器的路由:
Route::get('profile', 'UserController@show')->middleware('auth');
然而,在控制器的構造方法中指定中間件會更為便捷。通過使用在控制器構造方法中的 `middleware` 方法,你可以很容易地將中間件指定給控制器操作。你甚至可以約束中間件只對控制器類中的某些特定方法生效:
class UserController extends Controller
{
/**
* 實例化一個新的控制器實例。
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('log')->only('index');
$this->middleware('subscribed')->except('store');
}
}
控制器同時也允許你使用閉包的方式注冊中間件。這個方法提供了一種便捷的方式,使你在不定義一個完整的中間件類的情況下就可以為一個單獨的控制器定義中間件:
$this->middleware(function ($request, $next) {
// ...
return $next($request);
});
> {tip} 你可以指定中間件到控制器的部分操作上,然而這樣可能意味著你的控制器會變得很大;或者,你可以考慮將控制器分成多個更小的控制器。
<a name="resource-controllers"></a>
## 資源控制器
Laravel 資源路由可以將典型的「CRUD」路由指定到一個控制器上,僅需一行代碼即可實現。比如,你可能希望創建一個控制器來處理所有應用保存的「相片」的 HTTP 請求。使用 Artisan 命令 `make:controller` ,就能快速創建這樣一個控制器:
php artisan make:controller PhotoController --resource
這個命令會在 `app/Http/Controllers/PhotoController.php` 路徑生成一個控制器。該控制器包含了各種可用的資源操作方法。
接下來,你可以給控制器注冊一個資源路由:
Route::resource('photos', 'PhotoController');
這個路由聲明會創建多個路由來處理各種各樣的資源操作。前面生成的控制器已經包含了這些操作的方法,同時還包括了 HTTP 動作和操作 URI 的注釋。
#### 資源控制器操作處理
動作 | 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
#### 指定資源模型
如果你使用了路由模型綁定,并且想在資源控制器的方法中對某個模型實例做類型約束,你可以在生成控制器的時候使用 `--model` 選項:
php artisan make:controller PhotoController --resource --model=Photo
#### 偽造表單方法
因為 HTML 表單不能生成 `PUT` , `PATCH` 或者 `DELETE` 請求,所以你需要添加一個隱藏域字段 `_method` 來偽造這些 HTTP 動作。 `method_field` 輔助函數可以幫你創建這個字段:
{{ method_field('PUT') }}
<a name="restful-partial-resource-routes"></a>
### 部分資源路由
當聲明一個資源路由時,你可以指定控制器處理部分操作,而不必使用全部默認操作:
Route::resource('photo', 'PhotoController', ['only' => [
'index', 'show'
]]);
Route::resource('photo', 'PhotoController', ['except' => [
'create', 'store', 'update', 'destroy'
]]);
<a name="restful-naming-resource-routes"></a>
### 命名資源路由
默認情況下,所有的資源控制器操作都有一個路由名稱;然而,你可以在參數選項中傳入一個 `names` 數組來重寫這些名稱:
Route::resource('photo', 'PhotoController', ['names' => [
'create' => 'photo.build'
]]);
<a name="restful-naming-resource-route-parameters"></a>
### 命名資源路由參數
默認情況下, `Route::resource` 會基于資源名稱的「單數」形式為資源路由生成路由參數。你可以在選項數組中傳入 `parameters` 參數,實現每個資源基礎中參數名稱的重寫。 `parameters` 應該是一個將資源名稱和參數名稱聯系在一起的關聯數組:
Route::resource('user', 'AdminUserController', ['parameters' => [
'user' => 'admin_user'
]]);
上例將會為 `show` 方法的路由生成如下的 URI :
/user/{admin_user}
<a name="restful-localizing-resource-uris"></a>
### 本地化資源 URI
默認情況下, `Route::resource` 將會用英文動詞創建資源 URI。如果你想本地化 `create` 和 `edit` 動作名,你可以使用 `Route::resourceVerbs` 方法,可以在 `AppServiceProvider` 的 `boot` 方法中實現:
use Illuminate\Support\Facades\Route;
/**
* 自定義任何應用服務。
*
* @return void
*/
public function boot()
{
Route::resourceVerbs([
'create' => 'crear',
'edit' => 'editar',
]);
}
動作名稱被自定義后,像 `Route::resource('fotos', 'PhotoController')` 這樣注冊的資源路由將會產生如下的 URI:
/fotos/crear
/fotos/{foto}/editar
<a name="restful-supplementing-resource-controllers"></a>
### 附加資源控制器
如果你想在默認的資源路由之外增加額外的資源控制器路由,你應該在調用 `Route::resource` 之前定義這些路由;否則, `resource` 方法可能會不小心覆蓋你的附加路由:
Route::get('photos/popular', 'PhotoController@method');
Route::resource('photos', 'PhotoController');
> {tip} 記住保持控制器的專一性。如果你需要典型的資源操作之外的方法,可以考慮將你的控制器分成兩個更小的控制器。
<a name="dependency-injection-and-controllers"></a>
## 依賴注入和控制器
#### 構造方法注入
Laravel 使用 [服務容器](/docs/{{version}}/container) 來解析所有的控制器。因此,你可以在控制器的構造方法中對任何依賴使用類型約束,聲明的依賴會自動被解析并注入控制器實例中:
<?php
namespace App\Http\Controllers;
use App\Repositories\UserRepository;
class UserController extends Controller
{
/**
* 用戶 repository 實例.
*/
protected $users;
/**
* 創建一個新的控制器實例。
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
}
當然,你也可以對任何的 [Laravel 契約](/docs/{{version}}/contracts) 使用類型約束。當容器解析契約的時候,就會使用類型約束。直接將依賴注入控制器可能會提供更好的可測試性,但這取決于你的項目的具體情況。
#### 方法注入
除了構造方法注入之外,你還可以在控制器方法中使用依賴類型約束。一個常見的用法就是將 `Illuminate\Http\Request` 實例注入到控制器方法中:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* 保存一個新用戶。
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$name = $request->name;
//
}
}
如果控制器方法需要從路由參數中獲取輸入內容,只需要在其他依賴后列出路由參數即可。比如,如果你的路由定義如下:
Route::put('user/{id}', 'UserController@update');
通過以下方式定義控制器方法,可以讓你在使用 `Illuminate\Http\Request` 類型約束的同時仍然可以獲取參數 `id`:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* 更新給定用戶的信息。
*
* @param Request $request
* @param string $id
* @return Response
*/
public function update(Request $request, $id)
{
//
}
}
<a name="route-caching"></a>
## 路由緩存
> {note} 基于閉包的路由并不能被緩存。如果要使用路由緩存,你必須將所有的閉包路由轉換成控制器類路由。
如果你的應用只用到了基于控制器的路由,那么你應該充分利用 Laravel 的路由緩存。使用路由緩存將極大地減少注冊全部應用路由的時間。某些情況下,路由注冊甚至可以快一百倍。要生成路由緩存,只需在 Artisan 命令行中執行 `route:cache` 命令:
php artisan route:cache
運行這個命令之后,每一次請求的時候都將會加載緩存的路由文件。記住,如果添加了新的路由,你需要刷新路由緩存。因此,你應該只在項目部署時才運行 `route:cache` 命令:
你可以使用 `route:clear` 命令清除路由緩存:
php artisan route:clear
## 譯者署名
| 用戶名 | 頭像 | 職能 | 簽名 |
|---|---|---|---|
| [@easyFroce](https://github.com/easyForce) | <img class="avatar-66 rm-style" src="https://s.gravatar.com/avatar/6c3b9c5876f09ef9603c6d64c503ca19?s=80"> | 翻譯 | LOL |
---
> {note} 歡迎任何形式的轉載,但請務必注明出處,尊重他人勞動共創開源社區。
>
> 轉載請注明:本文檔由 Laravel China 社區 [laravel-china.org](https://laravel-china.org) 組織翻譯,詳見 [翻譯召集帖](https://laravel-china.org/topics/5756/laravel-55-document-translation-call-come-and-join-the-translation)。
>
> 文檔永久地址: https://d.laravel-china.org
- 說明
- 翻譯說明
- 發行說明
- 升級說明
- 貢獻導引
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- HomeStead
- Valet
- 核心架構
- 請求周期
- 服務容器
- 服務提供者
- 門面(Facades)
- Contracts
- 基礎功能
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- 重定向
- Session
- 表單驗證
- 錯誤與日志
- 前端開發
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全
- 用戶認證
- API認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- API 資源
- 序列化
- 測試
- 快速入門
- HTTP 測試
- 瀏覽器測試 Dusk
- 數據庫測試
- 測試模擬器
- 官方擴展包
- Cashier 交易工具包
- Envoy 部署工具
- Horizon
- Passport OAuth 認證
- Scout 全文搜索
- Socialite 社交化登錄
- 交流說明