編程學習網 > PHP技術 > Yii1 > Yii框架官方指南系列7——基礎知識:控制器
2014
11-07

Yii框架官方指南系列7——基礎知識:控制器

控制器是 CController 或其子類的實例。它在當用戶請求時由應用創建。 當一個控制器運行時,它執行所請求的動作,動作通常會引入所必要的模型并渲染相應的視圖。 動作 的最簡形式,就是一個名字以 action 開頭的控制器類方法。

控制器通常有一個默認的動作。當用戶的請求未指定要執行的動作時,默認動作將被執行。 默認情況下,默認的動作名為 index。它可以通過設置 CController::defaultAction 修改。

如下是一個控制器類所需的最簡代碼。由于此控制器未定義任何動作,對它的請求將拋出一個異常。

class SiteController extends CController
{
}

1. 路由

控制器和動作以 ID 識別??刂破?ID 是一種 'path/to/xyz' 的格式,對應相應的控制器類文件protected/controllers/path/to/XyzController.php, 其中的標志 xyz 應被替換為實際的名字 (例如post對應 protected/controllers/PostController.php). 動作 ID 是除去 action 前綴的動作方法名。例如,如果一個控制器類含有一個名為actionEdit的方法,則相應的動作 ID 為 edit。

注意: 在 1.0.3 版本之前,控制器 ID 的格式為 path.to.xyz ,而不是 path/to/xyz。

用戶以路由的形式請求特定的控制器和動作。路由是由控制器 ID 和動作 ID 連接起來的,兩者以斜線分割。 例如,路由post/edit 代表PostController 及其edit動作。默認情況下,URL http://hostname/index.php?r=post/edit即請求此控制器和動作。

注意: 默認情況下,路由是大小寫敏感的,從版本 1.0.1 開始,可以通過設置應用配置中的CUrlManager::caseSensitive 為 false 使路由對大小寫不敏感。當在大小寫不敏感模式中時, 要確保你遵循了相應的規則約定,即:包含控制器類文件的目錄名小寫,且 控制器映射 和 動作映射 中使用的鍵為小寫。

從 1.0.3 版本開始,應用可以含有 模塊(Module)。模塊中,控制器動作的路由格式為moduleID/controllerID/actionID 。 更多詳情,請閱讀 模塊相關章節.

2. 控制器實例化

控制器實例在 CWebApplication 處理到來的請求時創建。指定了控制器 ID , 應用將使用如下規則確定控制器的類以及類文件的位置。

  • 如果指定了 CWebApplication::catchAllRequest , 控制器將基于此屬性創建, 而用戶指定的控制器 ID 將被忽略。這通常用于將應用設置為維護狀態并顯示一個靜態提示頁面。
  • 如果在 CWebApplication::controllerMap 中找到了 ID, 相應的控制器配置將被用于創建控制器實例。
  • 如果 ID 為 'path/to/xyz'的格式,控制器類的名字將判斷為 XyzController, 相應的類文件則為protected/controllers/path/to/XyzController.php。例如, 控制器 ID admin/user 將被解析為控制器類 UserController,類文件是 protected/controllers/admin/UserController.php。 如果類文件不存在,將觸發一個 404 CHttpException 異常。

在使用了 模塊 (1.0.3 版后可用) 后,上述過程則稍有不同。 具體來說,應用將檢查此 ID 是否代表一個模塊中的控制器。如果是的話,模塊實例將被首先創建,然后創建模塊中的控制器實例

3. 動作

如前文所述,動作可以被定義為一個以 action單詞作為前綴命名的方法。而更高級的方式是定義一個動作類并讓控制器在收到請求時將其實例化。 這使得動作可以被復用,提高了可復用度。

要定義一個新動作類,可用如下代碼:

class UpdateAction extends CAction
{
    public function run()
    {
        // place the action logic here
    }
}

為了讓控制器注意到這個動作,我們要用如下方式覆蓋控制器類的actions() 方法:

class PostController extends CController
{
    public function actions()
    {
        return array(
            'edit'=>'application.controllers.post.UpdateAction',
        );
    }
}

如上所示,我們使用了路徑別名 application.controllers.post.UpdateAction 指定動作類文件為protected/controllers/post/UpdateAction.php.

通過編寫基于類的動作,我們可以將應用組織為模塊的風格。例如, 如下目錄結構可用于組織控制器相關代碼:

protected/
    controllers/
        PostController.php
        UserController.php
        post/
            CreateAction.php
            ReadAction.php
            UpdateAction.php
        user/
            CreateAction.php
            ListAction.php
            ProfileAction.php
            UpdateAction.php

動作參數綁定

從版本 1.1.4 開始,Yii 提供了對自動動作參數綁定的支持。 就是說,控制器動作可以定義命名的參數,參數的值將由 Yii 自動從 $_GET 填充。

為了詳細說明此功能,假設我們需要為 PostController 寫一個 create 動作。此動作需要兩個參數:

  • category: 一個整數,代表帖子(post)要發表在的那個分類的ID。
  • language: 一個字符串,代表帖子所使用的語言代碼。

從 $_GET 中提取參數時,我們可以不再下面這種無聊的代碼了:

class PostController extends CController
{
    public function actionCreate()
    {
        if(isset($_GET['category']))
            $category=(int)$_GET['category'];
        else
            throw new CHttpException(404,'invalid request');

        if(isset($_GET['language']))
            $language=$_GET['language'];
        else
            $language='en';

        // ... fun code starts here ...
    }
}

現在使用動作參數功能,我們可以更輕松的完成任務:

class PostController extends CController
{
    public function actionCreate($category, $language='en')
    {
        $category=(int)$category;

        // ... fun code starts here ...
    }
}

注意我們在動作方法 actionCreate 中添加了兩個參數。 這些參數的名字必須和我們想要從 $_GET 中提取的名字一致。 當用戶沒有在請求中指定 $language 參數時,這個參數會使用默認值 en 。 由于 $category 沒有默認值,如果用戶沒有在 $_GET 中提供 category 參數, 將會自動拋出一個 CHttpException (錯誤代碼 400) 異常。從版本1.1.5開始, Yii還支持數組類型的動作參數綁定。 這是通過PHP的類型約束來實現的,語法如下:

class PostController extends CController
{
    public function actionCreate(array $categories)
    {
        // Yii will make sure $categories be an array
    }
}

也就是說我們在方法參數聲明里的$categories之前添加了array關鍵字。這樣的話,如果$_GET['categories']只是一個簡單的字符串,它將會被轉化為一個包含該字符串的數組。

注意: 如果參數聲明沒有加上 array 類型約束, 意味著參數必須是標量 (i.e., not an array)。這種情況下,通過 $_GET 傳入一個數組參數將會引發HTTP異常。

4. 過濾器

過濾器是一段代碼,可被配置在控制器動作執行之前或之后執行。例如, 訪問控制過濾器將被執行以確保在執行請求的動作之前用戶已通過身份驗證;性能過濾器可用于測量控制器執行所用的時間。

一個動作可以有多個過濾器。過濾器執行順序為它們出現在過濾器列表中的順序。過濾器可以阻止動作及后面其他過濾器的執行

過濾器可以定義為一個控制器類的方法。方法名必須以 filter 開頭。例如,現有的 filterAccessControl 方法定義了一個名為 accessControl 的過濾器。 過濾器方法必須為如下結構:

public function filterAccessControl($filterChain)
{
    // 調用 $filterChain->run() 以繼續后續過濾器與動作的執行。
}

其中的 $filterChain (過濾器鏈)是一個 CFilterChain 的實例,代表與所請求動作相關的過濾器列表。在過濾器方法中, 我們可以調用 $filterChain->run() 以繼續執行后續過濾器和動作。

過濾器也可以是一個 CFilter 或其子類的實例。如下代碼定義了一個新的過濾器類:

class PerformanceFilter extends CFilter
{
    protected function preFilter($filterChain)
    {
        // 動作被執行之前應用的邏輯
        return true; // 如果動作不應被執行,此處返回 false
    }

    protected function postFilter($filterChain)
    {
        // 動作執行之后應用的邏輯
    }
}

要對動作應用過濾器,我們需要覆蓋 CController::filters() 方法。此方法應返回一個過濾器配置數組。例如:

class PostController extends CController
{
    ......
    public function filters()
    {
        return array(
            'postOnly + edit, create',
            array(
                'application.filters.PerformanceFilter - edit, create',
                'unit'=>'second',
            ),
        );
    }
}

上述代碼指定了兩個過濾器: postOnly 和 PerformanceFilter。 postOnly 過濾器是基于方法的(相應的過濾器方法已在 CController 中定義); 而 performanceFilter 過濾器是基于對象的。路徑別名application.filters.PerformanceFilter 指定過濾器類文件是protected/filters/PerformanceFilter。我們使用一個數組配置 PerformanceFilter ,這樣它就可被用于初始化過濾器對象的屬性值。此處 PerformanceFilter 的 unit 屬性值將被初始為 second。

使用加減號,我們可指定哪些動作應該或不應該應用過濾器。上述代碼中, postOnly 應只被應用于 edit 和 create動作,而 PerformanceFilter 應被應用于 除了 edit 和 create 之外的動作。 如果過濾器配置中沒有使用加減號,則此過濾器將被應用于所有動作。

附圖:控制器的run方法執行過程

controller的run方法執行過程

掃碼二維碼 獲取免費視頻學習資料

Python編程學習

查 看2022高級編程視頻教程免費獲取