控制器(Controllers)

2018-02-24 15:40 更新

控制器

控制器是?MVC?模式中的一部分, 是繼承yii\base\Controller類的對(duì)象,負(fù)責(zé)處理請(qǐng)求和生成響應(yīng)。 具體來(lái)說,控制器從應(yīng)用主體接管控制后會(huì)分析請(qǐng)求數(shù)據(jù)并傳送到模型, 傳送模型結(jié)果到視圖,最后生成輸出響應(yīng)信息。

操作

控制器由?操作?組成,它是執(zhí)行終端用戶請(qǐng)求的最基礎(chǔ)的單元,一個(gè)控制器可有一個(gè)或多個(gè)操作。

如下示例顯示包含兩個(gè)操作view?and?create?的控制器post

namespace app\controllers;

use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;

class PostController extends Controller
{
    public function actionView($id)
    {
        $model = Post::findOne($id);
        if ($model === null) {
            throw new NotFoundHttpException;
        }

        return $this->render('view', [
            'model' => $model,
        ]);
    }

    public function actionCreate()
    {
        $model = new Post;

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        } else {
            return $this->render('create', [
                'model' => $model,
            ]);
        }
    }
}

在操作?view?(定義為?actionView()?方法)中, 代碼首先根據(jù)請(qǐng)求模型ID加載?模型, 如果加載成功,會(huì)渲染名稱為view視圖并顯示,否則會(huì)拋出一個(gè)異常。

在操作?create?(定義為?actionCreate()?方法)中, 代碼相似. 先將請(qǐng)求數(shù)據(jù)填入模型, 然后保存模型,如果兩者都成功,會(huì)跳轉(zhuǎn)到ID為新創(chuàng)建的模型的view操作,否則顯示提供用戶輸入的create視圖。

路由

終端用戶通過所謂的路由尋找到操作,路由是包含以下部分的字符串:

  • 模型ID: 僅存在于控制器屬于非應(yīng)用的模塊;
  • 控制器ID: 同應(yīng)用(或同模塊如果為模塊下的控制器)下唯一標(biāo)識(shí)控制器的字符串;
  • 操作ID: 同控制器下唯一標(biāo)識(shí)操作的字符串。

路由使用如下格式:

ControllerID/ActionID

如果屬于模塊下的控制器,使用如下格式:

ModuleID/ControllerID/ActionID

如果用戶的請(qǐng)求地址為?http://hostname/index.php?r=site/index, 會(huì)執(zhí)行site?控制器的index?操作。 更多關(guān)于處理路由的詳情請(qǐng)參閱?路由?一節(jié)。

創(chuàng)建控制器

在yii\web\Application網(wǎng)頁(yè)應(yīng)用中,控制器應(yīng)繼承yii\web\Controller 或它的子類。 同理在yii\console\Application控制臺(tái)應(yīng)用中,控制器繼承yii\console\Controller 或它的子類。 如下代碼定義一個(gè)?site?控制器:

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
}

控制器ID

通常情況下,控制器用來(lái)處理請(qǐng)求有關(guān)的資源類型,因此控制器ID通常為和資源有關(guān)的名詞。 例如使用article作為處理文章的控制器ID。

控制器ID應(yīng)僅包含英文小寫字母、數(shù)字、下劃線、中橫杠和正斜杠, 例如?article?和?post-comment?是真是的控制器ID,article?,?PostComment,?admin\post不是控制器ID。

控制器Id可包含子目錄前綴,例如?admin/article?代表 yii\base\Application::controllerNamespace控制器命名空間下?admin子目錄中?article?控制器。 子目錄前綴可為英文大小寫字母、數(shù)字、下劃線、正斜杠,其中正斜杠用來(lái)區(qū)分多級(jí)子目錄(如panels/admin)。

控制器類命名

控制器ID遵循以下規(guī)則衍生控制器類名:

  • 將用正斜杠區(qū)分的每個(gè)單詞第一個(gè)字母轉(zhuǎn)為大寫。注意如果控制器ID包含正斜杠,只將最后的正斜杠后的部分第一個(gè)字母轉(zhuǎn)為大寫;
  • 去掉中橫杠,將正斜杠替換為反斜杠;
  • 增加Controller后綴;
  • 在前面增加yii\base\Application::controllerNamespace控制器命名空間.

下面為一些示例,假設(shè)yii\base\Application::controllerNamespace控制器命名空間為?app\controllers:

  • article?對(duì)應(yīng)?app\controllers\ArticleController;
  • post-comment?對(duì)應(yīng)?app\controllers\PostCommentController;
  • admin/post-comment?對(duì)應(yīng)?app\controllers\admin\PostCommentController;
  • adminPanels/post-comment?對(duì)應(yīng)?app\controllers\adminPanels\PostCommentController.

控制器類必須能被?自動(dòng)加載,所以在上面的例子中, 控制器article?類應(yīng)在?別名?為@app/controllers/ArticleController.php的文件中定義, 控制器admin/post2-comment應(yīng)在@app/controllers/admin/Post2CommentController.php文件中。

補(bǔ)充: 最后一個(gè)示例?admin/post2-comment?表示你可以將控制器放在 yii\base\Application::controllerNamespace控制器命名空間下的子目錄中, 在你不想用?模塊?的情況下給控制器分類,這種方式很有用。

控制器部署

可通過配置 yii\base\Application::controllerMap 來(lái)強(qiáng)制上述的控制器ID和類名對(duì)應(yīng), 通常用在使用第三方不能掌控類名的控制器上。

配置?應(yīng)用配置?中的application configuration,如下所示:

[
    'controllerMap' => [
        // 用類名申明 "account" 控制器
        'account' => 'app\controllers\UserController',

        // 用配置數(shù)組申明 "article" 控制器
        'article' => [
            'class' => 'app\controllers\PostController',
            'enableCsrfValidation' => false,
        ],
    ],
]

默認(rèn)控制器

每個(gè)應(yīng)用有一個(gè)由yii\base\Application::defaultRoute屬性指定的默認(rèn)控制器; 當(dāng)請(qǐng)求沒有指定?路由,該屬性值作為路由使用。 對(duì)于yii\web\Application網(wǎng)頁(yè)應(yīng)用,它的值為?'site', 對(duì)于 yii\console\Application控制臺(tái)應(yīng)用,它的值為?help, 所以URL為http://hostname/index.php?表示由?site?控制器來(lái)處理。

可以在?應(yīng)用配置?中修改默認(rèn)控制器,如下所示:

[
    'defaultRoute' => 'main',
]

創(chuàng)建操作

創(chuàng)建操作可簡(jiǎn)單地在控制器類中定義所謂的?操作方法?來(lái)完成,操作方法必須是以action開頭的公有方法。 操作方法的返回值會(huì)作為響應(yīng)數(shù)據(jù)發(fā)送給終端用戶,如下代碼定義了兩個(gè)操作?index?和?hello-world:

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
    public function actionIndex()
    {
        return $this->render('index');
    }

    public function actionHelloWorld()
    {
        return 'Hello World';
    }
}

操作ID

操作通常是用來(lái)執(zhí)行資源的特定操作,因此,操作ID通常為動(dòng)詞,如view,?update等。

操作ID應(yīng)僅包含英文小寫字母、數(shù)字、下劃線和中橫杠,操作ID中的中橫杠用來(lái)分隔單詞。 例如view,?update2,?comment-post是真實(shí)的操作ID,view?,?Update不是操作ID.

可通過兩種方式創(chuàng)建操作ID,內(nèi)聯(lián)操作和獨(dú)立操作. An inline action is 內(nèi)聯(lián)操作在控制器類中定義為方法;獨(dú)立操作是繼承yii\base\Action或它的子類的類。 內(nèi)聯(lián)操作容易創(chuàng)建,在無(wú)需重用的情況下優(yōu)先使用; 獨(dú)立操作相反,主要用于多個(gè)控制器重用,或重構(gòu)為擴(kuò)展

內(nèi)聯(lián)操作

內(nèi)聯(lián)操作指的是根據(jù)我們剛描述的操作方法。

操作方法的名字是根據(jù)操作ID遵循如下規(guī)則衍生:

  • 將每個(gè)單詞的第一個(gè)字母轉(zhuǎn)為大寫;
  • 去掉中橫杠;
  • 增加action前綴.

例如index?轉(zhuǎn)成?actionIndex,?hello-world?轉(zhuǎn)成?actionHelloWorld。

注意: 操作方法的名字大小寫敏感,如果方法名稱為ActionIndex不會(huì)認(rèn)為是操作方法, 所以請(qǐng)求index操作會(huì)返回一個(gè)異常,也要注意操作方法必須是公有的,私有或者受保護(hù)的方法不能定義成內(nèi)聯(lián)操作。

因?yàn)槿菀讋?chuàng)建,內(nèi)聯(lián)操作是最常用的操作,但是如果你計(jì)劃在不同地方重用相同的操作, 或者你想重新分配一個(gè)操作,需要考慮定義它為獨(dú)立操作。

獨(dú)立操作

獨(dú)立操作通過繼承yii\base\Action或它的子類來(lái)定義。 例如Yii發(fā)布的yii\web\ViewAction和yii\web\ErrorAction都是獨(dú)立操作。

要使用獨(dú)立操作,需要通過控制器中覆蓋yii\base\Controller::actions()方法在action map中申明,如下例所示:

public function actions()
{
    return [
        // 用類來(lái)申明"error" 操作
        'error' => 'yii\web\ErrorAction',

        // 用配置數(shù)組申明 "view" 操作
        'view' => [
            'class' => 'yii\web\ViewAction',
            'viewPrefix' => '',
        ],
    ];
}

如上所示,?actions()?方法返回鍵為操作ID、值為對(duì)應(yīng)操作類名或數(shù)組configurations?的數(shù)組。 和內(nèi)聯(lián)操作不同,獨(dú)立操作ID可包含任意字符,只要在actions()?方法中申明.

為創(chuàng)建一個(gè)獨(dú)立操作類,需要繼承yii\base\Action 或它的子類,并實(shí)現(xiàn)公有的名稱為run()的方法,?run()?方法的角色和操作方法類似,例如:

<?php
namespace app\components;

use yii\base\Action;

class HelloWorldAction extends Action
{
    public function run()
    {
        return "Hello World";
    }
}

操作結(jié)果

操作方法或獨(dú)立操作的run()方法的返回值非常重要,它表示對(duì)應(yīng)操作結(jié)果。

返回值可為?響應(yīng)?對(duì)象,作為響應(yīng)發(fā)送給終端用戶。

  • 對(duì)于yii\web\Application網(wǎng)頁(yè)應(yīng)用,返回值可為任意數(shù)據(jù), 它賦值給yii\web\Response::data, 最終轉(zhuǎn)換為字符串來(lái)展示響應(yīng)內(nèi)容。
  • 對(duì)于yii\console\Application控制臺(tái)應(yīng)用,返回值可為整數(shù), 表示命令行下執(zhí)行的 yii\console\Response::exitStatus 退出狀態(tài)。

在上面的例子中,操作結(jié)果都為字符串,作為響應(yīng)數(shù)據(jù)發(fā)送給終端用戶,下例顯示一個(gè)操作通過 返回響應(yīng)對(duì)象(因?yàn)閥ii\web\Controller::redirect()方法返回一個(gè)響應(yīng)對(duì)象)可將用戶瀏覽器跳轉(zhuǎn)到新的URL。

public function actionForward()
{
    // 用戶瀏覽器跳轉(zhuǎn)到 http://example.com
    return $this->redirect('http://example.com');
}

操作參數(shù)

內(nèi)聯(lián)操作的操作方法和獨(dú)立操作的?run()?方法可以帶參數(shù),稱為操作參數(shù)。 參數(shù)值從請(qǐng)求中獲取,對(duì)于yii\web\Application網(wǎng)頁(yè)應(yīng)用, 每個(gè)操作參數(shù)的值從$_GET中獲得,參數(shù)名作為鍵; 對(duì)于yii\console\Application控制臺(tái)應(yīng)用, 操作參數(shù)對(duì)應(yīng)命令行參數(shù)。

如下例,操作view?(內(nèi)聯(lián)操作) 申明了兩個(gè)參數(shù)?$id?和?$version。

namespace app\controllers;

use yii\web\Controller;

class PostController extends Controller
{
    public function actionView($id, $version = null)
    {
        // ...
    }
}

操作參數(shù)會(huì)被不同的參數(shù)填入,如下所示:

  • http://hostname/index.php?r=post/view&id=123:?$id?會(huì)填入'123',$version?仍為 null 空因?yàn)闆]有version請(qǐng)求參數(shù);
  • http://hostname/index.php?r=post/view&id=123&version=2: $id?和?$version?分別填入?'123'?和?'2'`;
  • http://hostname/index.php?r=post/view: 會(huì)拋出yii\web\BadRequestHttpException 異常 因?yàn)檎?qǐng)求沒有提供參數(shù)給必須賦值參數(shù)$id;
  • http://hostname/index.php?r=post/view&id[]=123: 會(huì)拋出yii\web\BadRequestHttpException 異常 因?yàn)?code>$id?參數(shù)收到數(shù)字值?['123']而不是字符串.

如果想讓操作參數(shù)接收數(shù)組值,需要指定$id為array,如下所示:

public function actionView(array $id, $version = null)
{
    // ...
}

現(xiàn)在如果請(qǐng)求為?http://hostname/index.php?r=post/view&id[]=123, 參數(shù)?$id?會(huì)使用數(shù)組值['123'], 如果請(qǐng)求為http://hostname/index.php?r=post/view&id=123, 參數(shù)?$id?會(huì)獲取相同數(shù)組值,因?yàn)闊o(wú)類型的'123'會(huì)自動(dòng)轉(zhuǎn)成數(shù)組。

上述例子主要描述網(wǎng)頁(yè)應(yīng)用的操作參數(shù),對(duì)于控制臺(tái)應(yīng)用,更多詳情請(qǐng)參閱控制臺(tái)命令。

默認(rèn)操作

每個(gè)控制器都有一個(gè)由 yii\base\Controller::defaultAction 屬性指定的默認(rèn)操作, 當(dāng)路由?只包含控制器ID,會(huì)使用所請(qǐng)求的控制器的默認(rèn)操作。

默認(rèn)操作默認(rèn)為?index,如果想修改默認(rèn)操作,只需簡(jiǎn)單地在控制器類中覆蓋這個(gè)屬性,如下所示:

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
    public $defaultAction = 'home';

    public function actionHome()
    {
        return $this->render('home');
    }
}

控制器生命周期

處理一個(gè)請(qǐng)求時(shí),應(yīng)用主體?會(huì)根據(jù)請(qǐng)求路由創(chuàng)建一個(gè)控制器,控制器經(jīng)過以下生命周期來(lái)完成請(qǐng)求:

  1. 在控制器創(chuàng)建和配置后,yii\base\Controller::init() 方法會(huì)被調(diào)用。
  2. 控制器根據(jù)請(qǐng)求操作ID創(chuàng)建一個(gè)操作對(duì)象:
    • 如果操作ID沒有指定,會(huì)使用yii\base\Controller::defaultAction默認(rèn)操作ID;
    • 如果在yii\base\Controller::actions()找到操作ID,會(huì)創(chuàng)建一個(gè)獨(dú)立操作;
    • 如果操作ID對(duì)應(yīng)操作方法,會(huì)創(chuàng)建一個(gè)內(nèi)聯(lián)操作;
    • 否則會(huì)拋出yii\base\InvalidRouteException異常。
  3. 控制器按順序調(diào)用應(yīng)用主體、模塊(如果控制器屬于模塊)、控制器的?beforeAction()?方法;
    • 如果任意一個(gè)調(diào)用返回false,后面未調(diào)用的beforeAction()會(huì)跳過并且操作執(zhí)行會(huì)被取消; action execution will be cancelled.
    • 默認(rèn)情況下每個(gè)?beforeAction()?方法會(huì)觸發(fā)一個(gè)?beforeAction?事件,在事件中你可以追加事件處理操作;
  4. 控制器執(zhí)行操作:
    • 請(qǐng)求數(shù)據(jù)解析和填入到操作參數(shù);
  5. 控制器按順序調(diào)用控制器、模塊(如果控制器屬于模塊)、應(yīng)用主體的?afterAction()?方法;
    • 默認(rèn)情況下每個(gè)?afterAction()?方法會(huì)觸發(fā)一個(gè)?afterAction?事件,在事件中你可以追加事件處理操作;
  6. 應(yīng)用主體獲取操作結(jié)果并賦值給響應(yīng).

最佳實(shí)踐

在設(shè)計(jì)良好的應(yīng)用中,控制器很精練,包含的操作代碼簡(jiǎn)短; 如果你的控制器很復(fù)雜,通常意味著需要重構(gòu),轉(zhuǎn)移一些代碼到其他類中。

歸納起來(lái),控制器

  • 可訪問?請(qǐng)求?數(shù)據(jù);
  • 可根據(jù)請(qǐng)求數(shù)據(jù)調(diào)用?模型?的方法和其他服務(wù)組件;
  • 可使用?視圖?構(gòu)造響應(yīng);
  • 不應(yīng)處理應(yīng)被模型處理的請(qǐng)求數(shù)據(jù);
  • 應(yīng)避免嵌入HTML或其他展示代碼,這些代碼最好在?視圖中處理.
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)