Yii2.0中文开发向导——控制器(Controller)

十度 Yii2 2015年11月30日 收藏

控制器(Controller)

本节包含以下方面的内容

  • 基本概念
  • 路由
    • 默认路由
  • 动作的参数
    • 在动作中定义参数
    • 从请求(request)中获取参数
  • 独立动作
  • 动作过滤器(Action Filters)
  • 捕获所有的请求
  • 自定义响应类


控制器(Controller)是应用程序中最关键的部分之一,它决定了如何处理传递进来的请求(Request),以及生成相应的响应(Response)。
大部分的控制器都会处理一个Http的请求,然后返回Html或者Json或者Xml格式的数据作为响应。


1、基本概念

控制器文件一般在应用程序的controllers目录下面,文件命名规则为XXXController.php,其中XXX可以为任意的名称,后面的Controller是固定格式,不能少一个单词。

一个基本的控制器的定义要从yii\web\Controller继承,如

namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
    public function actionIndex()
    {
        // will render view from "views/site/index.php"
        return $this->render('index');
    }
    public function actionTest()
    {
        // will just print "test" to the browser
        return 'test';
    }
}


一般来说控制器里面定义的动作都是 actionSomething格式的公共的方法。输出的数据可以是一个字符串或者yii\web\Response的实例。

动作输出的结果将会由 response组件处理并转换成不同的数据格式如JSON等。默认情况下是没有对动作的执行结果进行处理的。


2、路由

每个控制器里面的动作都有一个相对应的路由,在上面的例子中actionIndex有一个对应的site/index路由,actionTest对应的路由是site/test。在这个路由中site为控制器的id,test为动作的id。

一般情况下可以通过这个格式的URL来访问控制器和动作。http://example.com/?r=controller/action。当然你可以完全自定义url的格式(URL Management.)。

如果控制器是在一个模块里面,那么对应的路由格式为module/controller/action

控制器还可以存在于应用程序或者模块的控制器目录下面的子目录里。这种路由的格式前面就需要加上相应的目录名称。例如在controllers/admin下面有个UserController,那么动作actionIndex相应的路由为admin/user/indexadmin/user为控制器的Id.

如果模块或者控制器或者动作都没有找到,Yii将会返回一个"not found" 页面,并且返回的Http代码为404。

注意:
如果模块名称或者控制器名称或者动作名称是用的骆驼格式的命名写法,那么路由里面的每个大写单词之间都要用“-”来连接。如
DateTimeController::actionFastForward 相应的路由为 date-time/fast-forward


1、默认路由

如果一个URl没有指定路由,如http://www.shouce.ren,那么Yii将会使用默认的路由。
Yii在应用程序里面定义了默认的路由。默认的路由为siteyii\web\Application:: $defaultRoute定义,即控制器SiteController将会被使用。

有默认的路由也就会有默认的动作了。每个控制器都有一个默认的动作。如果在URL中没有指定动作的话,那么将会调用默认的动作。
默认的动作名为index,在yii\base\Controller:: $defaultAction中定义。
如http://www.shouce.ren/?r=site,只指定了路由为site,那么将会使用默认的动作index,即将会调用SiteController里面的actionIndex方法


3、动作参数

就像上面提到的一样,一个简单的动作就是一个以actionXXX格式命名的公开的方法。那么动作如何从Http的请求中获取想要的参数呢?

1、动作中定义参数

在定义动作的时候直接定义参数。这个参数的值会直接从$_GET里面获取对应的值。也就是说动作里面定义的参数只能从$_GET里面获取值。

namespace app\controllers;
use yii\web\Controller;
class BlogController extends Controller
{
    public function actionView($id, $version = null)
    {
        $post = Post::findOne($id);
        $text = $post->text;
        if ($version) {
            $text = $post->getHistory($version);
        }
        return $this->render('view', [
            'post' => $post,
            'text' => $text,
        ]);
    }
}

如上所示动作view定义了两个参数$id$version。其中$version的默认值为null。
我们可以通过http://www.shouce.ren/?r=blog/view&id=42或者http://www.shouce.ren/?r=blog/view&id=42&version=3来访问。

在第一种情况下由于没有version参数,将会使用在定义的时候的默认值。而在第二种情况下actionView里面将得到相应的值,$id为42,$version为3.

如果在动作里面定义的参数没有默认值,而访问的URL里面又没有相对应的变量,那么会抛一个异常。如http://www.shouce.ren/?r=blog/view由于没有在URL中指定id,在action的定义中又没有默认的值,所以将会抛一个异常。

2、从请求(request)中获取参数

如果要从POST中获取参数或者GET里面的参数太多,还可以c通过Yii里面的request对象来获取相关的参数,可以通过\Yii:: $app->request来访问。如下代码所示:

namespace app\controllers;
use yii\web\Controller;
use yii\web\HttpException;
class BlogController extends Controller
{
    public function actionUpdate($id)
    {
        $post = Post::findOne($id);
        if (!$post) {
            throw new NotFoundHttpException();
        }
        if (\Yii::$app->request->isPost) {
            $post->load(Yii::$app->request->post());
            if ($post->save()) {
                return $this->redirect(['view', 'id' => $post->id]);
            }
        }
        return $this->render('update', ['post' => $post]);
    }
}


4、独立动作(action)

如果一个动作是通用的,要想在其它的控制器中重复使用,可以把这个动作放在一个单独的文件中实现。

创建actions/Page.php

namespace app\actions;
class Page extends \yii\base\Action
{
    public $view = 'index';
    public function run()
    {
        return $this->controller->render($view);
    }
}

使用:

class SiteController extends \yii\web\Controller
{
    public function actions()
    {
        return [
            'about' => [
                'class' => 'app\actions\Page',
                'view' => 'about',
            ],
        ];
    }
}

actions()返回的是一个name-value数组,name为动作(action)的名称,class为实现的动作的类,view为action要使用的模板文件。

访问:
http://www.shouce.ren/?r=site/about


5、动作过滤器(Action Filters)

你可能需要对控制器里面的某些动作应用一些过滤器,例如判断对当前动作的访问权限,对动作返回结果进一步处理等。

动作过滤器是yii\base\ActionFilter的子类的实例。

要使用动作过滤器,可以把它像行为一样附加在控制器或者模块上。下面这个例子实现了对index动作的缓存

public function behaviors()
{
    return [
        'httpCache' => [
            'class' => \yii\filters\HttpCache::className(),
            'only' => ['index'],
            'lastModified' => function ($action, $params) {
                $q = new \yii\db\Query();
                return $q->from('user')->max('updated_at');
            },
        ],
    ];
}


你还可以同时使用多个动作过滤器。他们将按照在behaviors()里面定义的先后顺序依次执行。如果其中的任意一个过滤器取消了动作的执行,那么当前过滤器后面的所有的过滤器将不会被执行。同时这个动作也不会执行。

当附加一个过滤器到控制器的时候,这个过滤器可以应该于当前控制器里面所有的动作。如果附加到模块或者应用程序,这个过滤器就可以应用于这个模块或者应用程序下面的所有的动作。

要创建动作过滤器,从yii\base\ActionFilter继承并实现beforeAction()afterAction()方法。beforAction将在action执行之前执行,同理afterAction将在action执行之后执行。如果beforeAction()返回false,那么当前过滤器后面的所有的过滤器都将不会被执行,并且action也不会被执行。

authorization 章节介绍了yii\filters\AccessControl 过滤器,caching章节介绍了yii\filters\PageCacheyii\filters\HttpCache过滤器。在实现自定义的过滤器的时候可以参考这些内置的过滤器的实现。


6、捕获所有的请求

有时候用一个控制器和动作来处理所有的请求是非常有用的。例如在网站在处于维护的情况下显示一个通知等。如果要实现这个功能只需要设置应用程序的catchAll属性就可以了。有两种方式可以设置catchAll属性,动态的设置或者在配置文件里面设置。

return [
    'id' => 'basic',
    'basePath' => dirname(__DIR__),
    // ...
    'catchAll' => [ // <-- here
        'offline/notice',
        'param1' => 'value1',
        'param2' => 'value2',
    ],
]

上面的例子是通过配置文件来设置的。offline/notice的意思是指使用OfflineController::actionNotice()param1param2 是传递给notice动作方法的参数。


7、自定义响应类

一般在动作执行完后会返回一个render之后的html内容,当然还可以返回特定的响应

namespace app\controllers;
use yii\web\Controller;
use app\components\web\MyCustomResponse; #extended from yii\web\Response
class SiteController extends Controller
{
    public function actionCustom()
    {
        /*
         * do your things here
         * since Response in extended from yii\base\Object, you can initialize its values by passing in
         * __constructor() simple array.
         */
        return new MyCustomResponse(['data' => $myCustomData]);
    }
}