HTTP 控制器

簡介

除了在單一的 routes.php 檔案中定義所有的請求處理邏輯之外,你可能希望使用控制器類別來組織此行為。控制器可將相關的 HTTP 請求處理邏輯組成一個類別。控制器通常存放在 app/Http/Controllers 此目錄中。

基礎控制器

這裡是一個基礎控制器類別的例子:

<?php namespace App\Http\Controllers;

use App\Http\Controllers\Controller;

class UserController extends Controller {

	/**
	 * 顯示所給定的使用者個人資料。
	 *
	 * @param  int  $id
	 * @return Response
	 */
	public function showProfile($id)
	{
		return view('user.profile', ['user' => User::findOrFail($id)]);
	}

}

我們可以路由前往控制器動作,就像這樣:

Route::get('user/{id}', 'UserController@showProfile');

注意: 所有的控制器都應該擴展基礎控制器類別。

控制器和命名空間

有一點非常重要,那就是我們毋需指明完整的控制器命名空間,在類別名稱中 App\Http\Controllers 之後的部分即可用於表示「根」命名空間。 RouteServiceProvider 預設會在包含根控制器命名空間的路由群組中,載入 routes.php 此一檔案。

若你要在 App\Http\Controllers 此目錄深層使用 PHP 命名空間以巢狀化或組織你的控制器,只要使用相對於 App\Http\Controllers 根命名空間的特定類別名稱即可。因此,若你的控制器類別全名為 App\Http\Controllers\Photos\AdminController,你可以像這樣註冊一個路由:

Route::get('foo', 'Photos\AdminController@method');

命名控制器路由

和封閉路由一樣,你也可以指定控制器路由的名稱。

Route::get('foo', ['uses' => 'FooController@method', 'as' => 'name']);

指向控制器行為的 URL

要產生一個指向控制器行為的 URL,可使用 action 輔助方法。

$url = action('App\Http\Controllers\FooController@method');

若你想僅使用相對於控制器命名空間的類別名稱中的一部分,來產生指向控制器行為的 URL,可用 URL 產生器註冊控制器的根命名空間。

URL::setRootControllerNamespace('App\Http\Controllers');

$url = action('FooController@method');

你可以使用 currentRouteAction 方法來存取正在執行的控制器行為名稱:

$action = Route::currentRouteAction();

控制器中介層

中介層 可在控制器路由中指定,例如:

Route::get('profile', [
	'middleware' => 'auth',
	'uses' => 'UserController@showProfile'
]);

此外,你也可以在控制器建構式中指定中介層 :

class UserController extends Controller {

	/**
	 * 建立一個新的 UserController 實例。
	 */
	public function __construct()
	{
		$this->middleware('auth');

		$this->middleware('log', ['only' => ['fooAction', 'barAction']]);

		$this->middleware('subscribed', ['except' => ['fooAction', 'barAction']]);
	}

}

內隱控制器

Laravel 讓你能輕易地定義單一路由來處理控制器中的每一項行為。首先,用 Route::controller 方法定義一個路由:

Route::controller('users', 'UserController');

controller 方法接受兩個參數。第一個參數是控制器欲處理的 base URI,第二個是控制器的類別名稱。接著只要在你的控制器中加入方法,並在名稱前冠上它們所回應的 HTTP 動詞。

class UserController extends Controller {

	public function getIndex()
	{
		//
	}

	public function postProfile()
	{
		//
	}

	public function anyLogin()
	{
		//
	}

}

index 方法會回應控制器處理的根 URI ,在這個例子中是 users

如果你的控制器行為包含多個字詞,你可以在 URI 中使用「破折號」語法來存取此行為。例如,下面這個在 UserController 中的控制器動作會回應 users/admin-profile 此一 URI :

public function getAdminProfile() {}

Assigning Route Names

If you would like to “name” some of the routes on the controller, you may pass a third argument to the controller method:

Route::controller('users', 'UserController', [
	'anyLogin' => 'user.login',
]);

RESTful 資源控制器

資源控制器可讓你無痛建立和資源相關的 RESTful 控制器。例如,你可能希望創建一個控制器,它可用來處理針對你的應用程式所儲存相片的 HTTP 請求。我們可以使用 make:controller Artisan 指令,快速創建這樣的控制器:

php artisan make:controller PhotoController

接著,我們註冊一個指向此控制器的資源路由:

Route::resource('photo', 'PhotoController');

此單一路由宣告創建了多個路由,用來處理各式各樣和相片資源相關的 RESTful 行為。同樣地,產生的控制器已有各種和這些行為繫結的方法,包含用來通知你它們處理了那些 URI 及動詞。

由資源控制器處理的行為

動詞 路徑 行為 路由名稱
GET /photo index photo.index
GET /photo/create create photo.create
POST /photo store photo.store
GET /photo/{photo} show photo.show
GET /photo/{photo}/edit edit photo.edit
PUT/PATCH /photo/{photo} update photo.update
DELETE /photo/{photo} destroy photo.destroy

自定資源路由

除此之外,你也可以指定讓路由僅處理一部分的行為:

Route::resource('photo', 'PhotoController',
				['only' => ['index', 'show']]);

Route::resource('photo', 'PhotoController',
				['except' => ['create', 'store', 'update', 'destroy']]);

所有的資源控制器行為預設都有個路由名稱。然而你可在選項中傳遞一個 names 陣列來重載這些名稱:

Route::resource('photo', 'PhotoController',
				['names' => ['create' => 'photo.build']]);

處理巢狀資源控制器

在你的路由宣告中使用「點」號來「巢狀化」資源控制器:

Route::resource('photos.comments', 'PhotoCommentController');

此路由會註冊一個「巢狀的」資源,可透過像 photos/{photos}/comments/{comments} 這樣的 URL 來存取。

class PhotoCommentController extends Controller {

	/**
	 * 顯示指定照片的評論。
	 *
	 * @param  int  $photoId
	 * @param  int  $commentId
	 * @return Response
	 */
	public function show($photoId, $commentId)
	{
		//
	}

}

在資源控制器中加入其他的路由

除了預設的資源路由外,若你還需要在資源控制器中加入其他路由,應該在呼叫 Route::resource 之前先定義它們:

Route::get('photos/popular', 'PhotoController@method');

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

相依注入和控制器

建構式注入

Laravel 服務容器 用於解析所有的 Laravel 控制器。因此,在建構式中,你可以對控制器需要的任何相依做型別限制:

<?php namespace App\Http\Controllers;

use Illuminate\Routing\Controller;
use App\Repositories\UserRepository;

class UserController extends Controller {

	/**
	 * 使用者儲存庫實例。
	 */
	protected $users;

	/**
	 * 創建新的控制器實例。
	 *
	 * @param  UserRepository  $users
	 * @return void
	 */
	public function __construct(UserRepository $users)
	{
		$this->users = $users;
	}

}

當然了,你也可以對任何的 Laravel contract 作型別限制。只要容器能解析它,你就可以對它作型別限制。

方法注入

除了建構器注入外,你也可以對控制器方法的相依作型別限制。例如,讓我們對某個方法的 Request 實例作型別限制:

<?php namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;

class UserController extends Controller {

	/**
	 * 儲存一個新的使用者。
	 *
	 * @param  Request  $request
	 * @return Response
	 */
	public function store(Request $request)
	{
		$name = $request->input('name');

		//
	}

}

如果你的控制器方法預期由路由參數取得輸入,只要在其他的相依之後列出路由參數即可:

<?php namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;

class UserController extends Controller {

	/**
	 * 更新特定使用者。
	 *
	 * @param  Request  $request
	 * @param  int  $id
	 * @return Response
	 */
	public function update(Request $request, $id)
	{
		//
	}

}

注意: 方法注入和 模型繫結 是完全相容的。容器可智慧地判斷那些參數和模型相關以及那些參數應該被注入。

路由快取

若控制器路由只由你的應用程式專用,你可利用 Laravel 的路由快取。使用路由快取,將大幅降低註冊應用程式所有路由所需要的時間。某些情況下,路由註冊甚至可以快上 100 倍。要產生路由快取,只要執行 route:cache Artisan 指令:

php artisan route:cache

就是這樣!你的快取路由檔案將會被用來代替 app/Http/routes.php 此一檔案。記住,若你增加了任何新的路由,你就 必須產生一個新的路由快取。因此在專案部署時,你可能會希望只要執行 route:cache 指令:

要移除路由快取檔案,但不希望產生新的快取,可使用 route:clear 指令:

php artisan route:clear