控制器 (Controllers)

基本 Controllers

除了在單一 routes.php 檔中定義所有路由層邏輯外,你可能也想利用控制器類別來整合這些行為模式。控制器可以統合相關的路由邏輯到同一個類別中,並且利用更先進的框架特性的優勢,如自動的 依賴注入

控制器一般存放在 app/controllers 目錄下,這個目錄已預設註冊在 composer.jsonclassmap 中。然而,技術上控制器可以放在任何目錄或是子目錄。路由宣告與控制器類別存放在哪個位址並無關係。所以,只要 Composer 知道如何自動載入控制器類別,你就可以放在任何你想要的地方。

下面是一個基本的控制器類別例子:

class UserController extends BaseController {

    /**
     * Show the profile for the given user.
     */
    public function showProfile($id)
    {
        $user = User::find($id);

        return View::make('user.profile', array('user' => $user));
    }

}

所有的控制器都應該繼承自 BaseController 類別。BaseController 也放在 app/controllers 目錄下,BaseController 可以作為放置共同控制器邏輯的地方。BaseController 繼承了框架的 Controller 類別。現在,我們可以像這樣將請求從路由導至控制器行為中:

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

如果你選擇使用 PHP 命名空間來分層或整合你的控制器,只要在定義路由時使用完整的類別名稱:

Route::get('foo', 'Namespace\FooController@method');

注意:既然我們使用 Composer 自動載入 PHP 類別,控制器可以放在檔案系統的任何地方,只要 Composer 知道如何載入他們。對你的應用程式而言,控制器目錄並沒有強制使用怎樣目錄結構。路由至控制器是完全與檔案系統脫鉤的。

你可以為控制器路由指定名稱:

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

你可以使用 URL::action 方法或者是 action 輔助方法來產生到控制器行為的 URL:

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

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

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

$action = Route::currentRouteAction();

控制器過濾器

過濾器(Filter) 在控制器路由中宣告方式,如同「一般」的路由一樣。

Route::get('profile', array('before' => 'auth',
            'uses' => 'UserController@showProfile'));

然而,你也可以在控制器中指定過濾器:

class UserController extends BaseController {

    /**
     * Instantiate a new UserController instance.
     */
    public function __construct()
    {
        $this->beforeFilter('auth', array('except' => 'getLogin'));

        $this->beforeFilter('csrf', array('on' => 'post'));

        $this->afterFilter('log', array('only' =>
                            array('fooAction', 'barAction')));
    }

}

你可以在控制器裡使用閉合函式定義過濾器:

class UserController extends BaseController {

    /**
     * Instantiate a new UserController instance.
     */
    public function __construct()
    {
        $this->beforeFilter(function()
        {
            //
        });
    }

}

如果你想使用控制器裡的方法當做過濾器,你可以使用 @ 語法定義過濾器:

class UserController extends BaseController {

    /**
     * Instantiate a new UserController instance.
     */
    public function __construct()
    {
        $this->beforeFilter('@filterRequests');
    }

    /**
     * Filter the incoming requests.
     */
    public function filterRequests($route, $request)
    {
        //
    }

}

RESTful 控制器

Laravel 讓你可以簡單的經由定義一個路由規則來處理控制器裡的所有遵照 REST 命名規範的行為。首先,使用 Route::controller 方法定義路由:

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

controller 方法可以接收兩個參數,第一個是控制器對應的基本 URI,第二個是控制器的類別名稱。接下來,只要把對應的 HTTP 請求動詞前綴加在控制器方法前:

class UserController extends BaseController {

    public function getIndex()
    {
        //
    }

    public function postProfile()
    {
        //
    }

    public function anyLogin()
    {
        //
    }

}

index 方法會對應到 controller 的根 URI,以上面的例子來說,就是 users

若你的控制器方法包含很多單字,你可以在 URI 使用「破折號(-)」來對應方法。例如 UserController 中,如下的方法會對應到 users/admin-profile URI:

public function getAdminProfile() {}

資源控制器

資源控制器可以簡單的建立跟資源相關的 RESTful 控制器。例如,你可能想要建立控制器管理應用程式裡儲存的照片。透過 Artisan 命令列工具裡的 controller:make 以及使用 Route::resource 方法,可以很快的建立 控制器。

從命令列執行下列命令建立控制器:

php artisan controller:make PhotoController

然後我們可以註冊一個資源化路由至控制器上:

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

此單一路由宣告創建了處理一系列對圖片資源的 RESTful 行為路由。同樣的,剛才產生的控制器對這些動作已經有預建的對應方法,以及註解告知對應的 URI 和所處理的請求動作。

資源控制器對應的動作

Verb Path Action Route Name
GET /resource index resource.index
GET /resource/create create resource.create
POST /resource store resource.store
GET /resource/{resource} show resource.show
GET /resource/{resource}/edit edit resource.edit
PUT/PATCH /resource/{resource} update resource.update
DELETE /resource/{resource} destroy resource.destroy

有時你可能只需要對應部分的資源的動作:

php artisan controller:make PhotoController --only=index,show

php artisan controller:make PhotoController --except=index

你也可以在路由宣告時指定需要對應的動作:

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

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

預設所有的資源控制器動作都有路由名稱,然而,你可以在選項傳入 names 陣列,覆寫這些名稱:

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

處理巢狀資源控制器

為了使用巢狀資源控制器,在路由宣告時使用「點」表示法:

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

這個路由規則會註冊一個「巢狀」資源,可以對應如下的 URLs:photos/{photoResource}/comments/{commentResource}

class PhotoCommentController extends BaseController {

    public function show($photoId, $commentId)
    {
        //
    }

}

增加額外路由規則到 Resource Controller

你果你需要增加額外的 route 規則到預設的 resource controller,你應該在宣告 Route::resource 之前宣告這些規則:

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

對應遺漏的方法

可以定義一個 catch-all 方法,當 controller 找不到對應的方法就會被呼叫,這個方法應該宣告為 missingMethod,會傳入請求的方法和參數陣列:

定義一個 Catch-All 方法

public function missingMethod($parameters = array())
{
    //
}