控制器
控制器
控制器
簡介
為了替代在路由文件中以閉包形式定義的所有的請求處理邏輯, 你也許想使用控制類來組織這些行為??刂破髂軐⑾嚓P(guān)的請求處理邏輯組成一個單獨(dú)的類。 控制器被存放在 app/Http/Controllers
目錄中。
基礎(chǔ)控制器
定義控制器
下面是一個基礎(chǔ)控制器類的例子。 需要注意的是,該控制器繼承了 Laravel 內(nèi)置的基類控制器。 該基類控制器提供了一些便利的方法,比如 middleware
方法,該方法可以為控制器行為添加中間件:
<?php namespace App\Http\Controllers; use App\User; use App\Http\Controllers\Controller; class UserController extends Controller { /** * 顯示給定用戶的概要文件. * * @param int $id * @return View */ public function show($id) { return view('user.profile', ['user' => User::findOrFail($id)]); } }
你可以這樣定義一個指向控制器行為的路由:
Route::get('user/{id}', 'UserController@show');
現(xiàn)在,當(dāng)一個請求與指定路由的 URI 匹配時, UserController
控制器中的 show
方法就會被執(zhí)行。路由參數(shù)也將會被傳遞給該方法。
{tip} 控制器并 不是強(qiáng)制要求繼承基礎(chǔ)類 。 但是, 如果控制器沒有繼承基礎(chǔ)類,你將無法使用一些便捷的功能,比如
middleware
,validate
和dispatch
方法。
控制器 & 命名空間
需要著重指出的是,在定義控制器路由時我們不需要指定完整的控制器命名空間。因為 RouteServiceProvider
會在一個包含命名空間的路由組中加載路由文件,我們只需要指定類名中 App\Http\Controllers
命名空間之后的部分就可以了。
如果你選擇將控制器放在 App\Http\Controllers
更深層次的目錄中,需要使用相對于 App\Http\Controllers
作為根命名空間的指定類名。 因此,如果你完整的控制器類名為 App\Http\Controllers\Photos\AdminController
,你在路由中應(yīng)當(dāng)采用如下的形式注冊:
Route::get('foo', 'Photos\AdminController@method');
單個行為控制器
如果你想定義一個只處理單個行為的控制器,你可以在控制器中放置一個 __invoke
方法:
<?php namespace App\Http\Controllers; use App\User; use App\Http\Controllers\Controller; class ShowProfile extends Controller { /** * 展示給定用戶的資料. * * @param int $id * @return View */ public function __invoke($id) { return view('user.profile', ['user' => User::findOrFail($id)]); } }
當(dāng)注冊單個行為控制器的路由時,無需指明方法:
Route::get('user/{id}', 'ShowProfile');
你可以通過 Artisan 命令工具里的 make:controller
命令中的 --invokable
選項來生成一個可調(diào)用的控制器:
php artisan make:controller ShowProfile --invokable
控制器中間件
Middleware 可以在路由文件中分配給控制器的路由。
Route::get('profile', 'UserController@show')->middleware('auth');
但是,在控制器的構(gòu)造函數(shù)中指定中間件更為方便。使用控制器構(gòu)造函數(shù)中的 middleware
方法,可以輕松地將中間件分配給控制器的操作。你甚至可以將中間件限制為控制器類上的某些方法。
class UserController extends Controller{ /** * Instantiate a new controller instance. * * @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} 你可以將中間件分配給控制器操作的一個子集;但是,它可能表明你的控制器正在變得很大。建議你將控制器拆分為多個較小的控制器。
資源控制器
Laravel 資源路由將典型的「CURD (增刪改查)」路由分配給具有單行代碼的控制器。 例如,你希望創(chuàng)建一個控制器來處理應(yīng)用保存的 "照片" 的所有 HTTP 請求。使用 Artisan 命令 make:controller
, 我們可以快速創(chuàng)建這樣一個控制器:
php artisan make:controller PhotoController --resource
這個命令會生成一個控制器 app/Http/Controllers/PhotoController.php
。 其中包括每個可用資源操作的方法。
接下來,你可以給控制器注冊一個資源路由:
Route::resource('photos', 'PhotoController');
這個單一的路由聲明創(chuàng)建多個路由來處理資源上的各種行為。生成的控制器為每個行為保留了方法,包括了關(guān)于處理 HTTP 動詞和 URLs 的聲明注釋。
你可以通過將數(shù)組傳參到 resources
方法中的方式來一次性的創(chuàng)建多個資源控制器:
Route::resources([ 'photos' => 'PhotoController', 'posts' => 'PostController' ]);
資源控制器操作處理
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 |
指定資源模型
如果你使用了路由模型綁定,并且想在資源控制器的方法中使用類型提示,你可以在生成控制器的時候使用 --model
選項:
php artisan make:controller PhotoController --resource --model=Photo
偽造表單方法
因為 HTML 表單不能生成 PUT
, PATCH
, 和 DELETE
請求,所以你需要一個添加一個隱藏的 _method
字段來偽造 HTTP 動作。這個 Blade 指令 @method
可以為你創(chuàng)建這個字段:
<form action="/foo/bar" method="POST"> @method('PUT') </form>
部分資源路由
聲明資源路由時,你可以指定控制器應(yīng)該處理的部分行為,而不是所有默認(rèn)的行為:
Route::resource('photos', 'PhotoController')->only([ 'index', 'show' ]); Route::resource('photos', 'PhotoController')->except([ 'create', 'store', 'update', 'destroy' ]);
API 資源路由
當(dāng)聲明用于 APIs 的資源路由時,通常需要排除顯示 HTML 模板的路由, 如 create
和 edit
。 為了方便起見,你可以使用 apiResource
方法自動排除這兩個路由:
Route::apiResource('photos', 'PhotoController');
你可以通過傳遞一個數(shù)組給 apiResources
方法的方式來一次性注冊多個 API 資源控制器:
Route::apiResources([ 'photos' => 'PhotoController', 'posts' => 'PostController' ]);
為了快速生成一個不包含 create
和 edit
方法的 API 資源控制器,可以在執(zhí)行 make:controller
命令時加上 --api
選項:
php artisan make:controller API/PhotoController --api
命名資源路由
默認(rèn)情況下,所有資源控制器操作都有一個路由名稱;但是,是可以通過用選項傳遞一個 names
數(shù)組來覆蓋這些名稱:
Route::resource('photos', 'PhotoController')->names([ 'create' => 'photos.build' ]);
命名資源路由參數(shù)
默認(rèn)情況下, Route::resource
會根據(jù)資源名稱的 「單數(shù)」 形式創(chuàng)建資源路由的路由參數(shù)。你可以在選項數(shù)組中傳入 parameters
參數(shù)來輕松地覆蓋每個資源。 parameters
數(shù)組應(yīng)當(dāng)是一個資源名稱和參數(shù)名稱的關(guān)聯(lián)數(shù)組:
Route::resource('users', 'AdminUserController')->parameters([ 'users' => 'admin_user' ]);
上列將會為資源的 show
路由生成如下的 URl :
/users/{admin_user}
本地化資源 URIs
默認(rèn)情況下, Route::resource
將會使用英文動詞來創(chuàng)建資源 URI 。如果你需要本地化 create
和 edit
行為動作名,你可以在 AppServiceProvider
的 boot
方法中使用 Route::resourceVerbs
方法實現(xiàn)。
use Illuminate\Support\Facades\Route; /** * 初始化任何應(yīng)用服務(wù) * * @return void */ public function boot(){ Route::resourceVerbs([ 'create' => 'crear', 'edit' => 'editar', ]); }
一旦動作被自定義后,像 Route::resource('fotos', 'PhotoController')
這樣注冊的資源路由將會產(chǎn)生如下的 URI:
/fotos/crear/fotos/{foto}/editar
補(bǔ)充資源控制器
如果你需要為資源控制器添加默認(rèn)路由之外的額外路由,你應(yīng)該調(diào)用 Route::resource
;之前定義這些路由;否則,由 resource
方法定義的路由可能會無意中優(yōu)先你補(bǔ)充的路由:
Route::get('photos/popular', 'PhotoController@method'); Route::resource('photos', 'PhotoController');
{tip} 記住保持控制器的專一性。如果你發(fā)現(xiàn)自己經(jīng)常需要典型資源操作之外的方法,請考慮將控制器拆分為較小的控制器。
依賴注入 & 控制器
構(gòu)造函數(shù)注入
Laravel 服務(wù)容器 解析所有的控制器。因此,你可以在控制器的構(gòu)造函數(shù)中使用類型提示可能需要的依賴項。依賴聲明會被自動解析并注入到控制器實例:
<?php namespace App\Http\Controllers; use App\Repositories\UserRepository; class UserController extends Controller{ /** * The user repository instance. */ protected $users; /** * Create a new controller instance. * * @param UserRepository $users * @return void */ public function __construct(UserRepository $users) { $this->users = $users; } }
當(dāng)然,你可以輸入任何的 Laravel 契約. 只要容器可以解析它。根據(jù)你的應(yīng)用,注入你的類型提示到控制器會提供更好可測試性。
方法注入
處理構(gòu)造函數(shù)注入,你還可以在控制器方法中輸入類型提示依賴項。方法注入最常見的用例是在控制器方法中注入 Illuminate\Http\Request
的實例:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class UserController extends Controller{ /** * Store a new user. * * @param Request $request * @return Response */ public function store(Request $request) { $name = $request->name; // } }
如果你的控制器還需要獲取路由參數(shù)中的輸入,把路由參數(shù)放在這些依賴項的后面。例如,你的路由定義像這樣:
Route::put('user/{id}', 'UserController@update');
你仍然可以輸入 Illuminate\Http\Request
類型提示,并通過在你的控制器方法中使用下面的定義來訪問 id
參數(shù):
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class UserController extends Controller{ /** * Update the given user. * * @param Request $request * @param string $id * @return Response */ public function update(Request $request, $id) { // } }
路由緩存
{note} 基于閉包的路由無法被緩存。要使用路由緩存。你需要將任何閉包路由轉(zhuǎn)換成控制器路由。
如果你的應(yīng)用只使用了基于控制器的路由,那么你應(yīng)該利用路由緩存。使用路由緩存將極大地減少注冊所有應(yīng)用路由所需的時間。某些情況下,路由注冊的速度甚至?xí)?100 倍。要生成路由緩存,只需要執(zhí)行 route:cache
:
php artisan route:cache
運(yùn)行此命令之后,每個請求都將加載緩存的路由文件。記住,如果你添加了任何的新路由,則需要生成新的路由緩存。因此,你只應(yīng)在項目部署期間運(yùn)行 route:cache
命令。
你可以使用 route:clear
命令來清除路由緩存:
php artisan route:clear