Controller ( Yii Framework )

在mvc的controller,主要是處理邏輯部份,會依照不同的path、參數、method…等,決定要處理的資料,以及要回傳什麼資訊給user。在Yii的controller都繼承至CController,需要導向哪個action,會在controller裡面設定完成。

  1. Route
  2. Action
  3. Filter

Route

在Yii裡的程式進入點,都是透過project/index.php這一隻程式,在根據webApp的confifg,決定預設執行哪支controller,以及在project/protected/controllers對應的path。

例如在project/index.php裡,會指定使用哪個config:

1
2
3
$config=dirname(__FILE__).'/protected/config/main.php';

Yii::createWebApplication($config)->run();

然後在main.php的config中,可以透過defaultController指定預設controller:

1
2
3
4
return Array(
'defaultController'=>'post',
//doSomething
);

意思就是當path為根目錄時,會導向至PostController,例如url為:

1
http://localhost/

與下面url相同,會藉由r這個參數對照contoller/action(當action未指定時,預設執行actionIndex這function,也可透過defaultAction自訂):

1
http://localhost/index.php?r=post/index

對應的project/protected/controllers/PostController

1
2
3
4
5
6
class PostController extends CController {

public function actionIndex(){
//doSomething...
}
}

如果想自訂controller對應class的路徑,可使用controllerMap


自訂url對應controller和action

使用自訂的url規則,去對應controller和action,可以修改webapp的config:

1
2
3
4
5
6
7
8
9
10
11
return array(
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'post/<id:\d+>/<title:.*?>'=>'post/view',
'posts/<tag:.*?>'=>'post/index',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
),
//doSomething...
);

nginx的config設定,將除了靜態檔案以外的request,全部導向至index.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
server {

listen 80 default;
server_name localhost;

access_log /var/log/nginx/localhost.access.log;

location ~* \.(js|png|jpg|gif|css|html)$ {

root /home/user/example/yii_example/yii/demos/blog;
}

location / {

index index.php index.html index.htm;
rewrite ^(.*) /index.php last;
}

location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
#fastcgi_param SCRIPT_FILENAME /tmp$fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME /home/user/example/yii_example/yii/demos/blog$fastcgi_script_name;
include fastcgi_params;
}
}

Action

yii根據path導向至不同的controller,在透過controller指派action去處理。一般來說action可以直接寫在controller裡,也可以透過繼承CAction的方式,在controller定義此class。

在controller裡面定義action,有兩種方式:

  • 只需要讓function名稱的開頭為action,在加上action的名稱即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class PostController extends CController {

    /**
    * path為
    /index.php?r=post
    /index.php?r=post/index
    **/
    public function actionIndex(){

    }

    /**
    * path為
    /index.php?r=post/create
    **/
    public function actionCreate(){

    }
    }
  • 透過actions這個function,回傳定義好的config,對應不同CAction

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class PostController extends CController {

    public function actions()
    {
    return array(
    // page action renders "static" pages stored under 'protected/views/site/pages'
    // They can be accessed via: index.php?r=site/page&view=FileName
    'page'=>array(
    'class'=>'CViewAction',
    ),
    // file path under 'protected/controllers/post/SayHelloAction'
    'sayhello'=>array(
    'class'=>'application.controllers.post.SayHelloAction',
    ),
    );
    }
    }

CAction

繼承至CAction的方式,如下:

1
2
3
4
5
6
7
class SayHelloAction extends CAction {

public function run()
{
//doSomething...
}
}

yii也提供一些現成的action可以使用,像是:

  • CCaptchaAction
    自動產生驗證碼,需要安裝php5-gd。

  • CViewAction
    可透過view這個參數,導向到不同頁面,而不需要針對每個頁面寫一個action。

Filter

yii的filter與action在controller的配置方式差不多,同樣可以在controller自訂filter的function,也可以class的方式繼承CFilter

在controller寫filter方式:

1
2
3
4
public function filterAccessControl($filterChain)
{
// call $filterChain->run() to continue filter and action execution
}

如果呼叫$filterChain->run(),將會繼續往下執行下個filter和action,反則會中斷掉。

與action在controller宣告不同的是,filter的套用,必須在controller裡定義filters這個function,回傳定義的config:

1
2
3
4
5
6
public function filters(){

return array(
"accessControl",
);
}

上面這種方式,將會套用此controller的所有action,若要套用個別action,如下:

1
2
3
4
5
6
7
public function filters(){

//將filter套用至edit和create action
return array(
"accessControl + edit, create",
);
}

如果把+替換成-,指的就是除了edit和create這兩個不套用filter,剩下全部套用。

CFilter

CFilter將會透過preFilter這個function,決定要繼續向下執行或者停止,而postFilter則是會在preFilter向下執行時,才會去呼叫。

定義一個filter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class PerformanceFilter extends CFilter
{

public $unit;

protected function preFilter($filterChain)
{
// logic being applied before the action is executed
//print $unit;
return true; // false if the action should not be executed
}

protected function postFilter($filterChain)
{
// logic being applied after the action is executed
}
}

在controller中使用PerformanceFilter:

1
2
3
4
5
6
7
8
9
10
11
12
13
class PostController extends CController
{
......
public function filters()
{
return array(
array(
'application.filters.PerformanceFilter - edit, create',
'unit'=>'second',
),
);
}
}

filter第二個傳入參數unit,將會設定PerformanceFilter的unit值。所以除了第一個參數,會設定filter,之後的參數都會對應filter的public屬性。