Yii2 理解Controller

    • 版本
    • 继承与实现
    • actions
    • createAction
    • getModules
    • runAction
    • run
    • findLayoutFile
    • render renderContent
    • 参考

1 版本

// yii\BaseYii\getVersion
public static function getVersion()
{
    return '2.0.10';
}

2 继承与实现

Controller继承与Component, 并实现了ViewContextInterface接口。
在Controller重要的有两块: action, view

3 actions

public function actions()
{
    return [];
}

在自定义的XXController类中可以看见各种actionXXX函数,比如actionIndex, actionCreate
而actions用于添加额外的action函数,如果有需要,派生类可以重载它:

public function actions()
{
    return [
        'error' => [
            'class' => 'yii\web\ErrorAction',
        ],
    ];
}

这样,只要再添加View/XX/error.php 就可以使用这个action了。

4 createAction

public function createAction($id)
{
    // $id相当于 index, create, update, 就是actionIndex, actionCreate。
    // 框架在使用method_exists查找的时候, 会加上action + $id, 并把$id的首字母大写
    // 如果$id为空, 则使用默认值'index'
    if ($id === '')
    {
        $id = $this->defaultAction;
    }
    // 优先查找额外添加的action
    $actionMap = $this->actions();
    if (isset($actionMap[$id]))
    {
        return Yii::createObject($actionMap[$id], [$id, $this]);
    }
    // $id 的命名规则:
    // 由a~z, 0~9, \, -, _ 组成, 且不能包含--
    // $id头尾都不能有-
    elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id)
    {
        // 组成函数名规则
        // 1 以action开头
        // 2 如果$id有用-连接, 则先用explode转为数组, 然后用implode组合到一起, 并将每个单词的首字母大写
        // 3 用str_replace将implode中的' '替换为''
        // 示例
        // get-your-name
        // explode: ['get', 'your', 'name']
        // implode: get your name
        // ucwords: Get Your Name
        // str_replace: GetYourName
        // 最终: actionGetYourName
        $methodName = 'action' . str_replace(' ', '', ucwords(implode(' ', explode('-', $id))));
        if (method_exists($this, $methodName))
        {
            $method = new \ReflectionMethod($this, $methodName);
            if ($method->isPublic() && $method->getName() === $methodName)
            {
                // InlineAction就是用于把动作当成是controller函数
                return new InlineAction($id, $this, $methodName);
            }
        }
    }

    return null;
}

5 getModules

该函数返回其主人,主人的主人…的组合

public function getModules()
{
    $modules = [$this->module];
    $module = $this->module;
    // 如果主人还有主人, 则继续查找下去
    while ($module->module !== null)
    {
        // 越古老的主人放在modules的越前面
        array_unshift($modules, $module->module);
        $module = $module->module;
    }
    return $modules;
}

6 runAction

public function runAction($id, $params = [])
{
    $action = $this->createAction($id);
    if ($action === null)
    {
        throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);
    }

    Yii::trace('Route to run: ' . $action->getUniqueId(), __METHOD__);

    if (Yii::$app->requestedAction === null)
    {
        Yii::$app->requestedAction = $action;
    }

    $oldAction = $this->action;
    $this->action = $action;

    $modules = [];
    $runAction = true;

    // getModules保存的主人顺序是 越古老越前面
    foreach ($this->getModules() as $module)
    {
        // beforeAction的返回值将会决定action是否继续执行下去
        // 如果有一个主人在beforeAction中决定不执行, 则action就不再执行
        if ($module->beforeAction($action))
        {
            // 现在将越年轻的主人放在越前面
            array_unshift($modules, $module);
        }
        else
        {
            $runAction = false;
            break;
        }
    }
    $result = null;
    // 所有主人都满足条件
    // 判断自己的beforeAction是否满足条件
    if ($runAction && $this->beforeAction($action))
    {
        // 运行这个action, 如果是自定义的action,一定要重载run函数
        // 这里面也有一个beforeRun, afterRun
        $result = $action->runWithParams($params);

        // 在beforeAction中,是优先执行更古老的主人,最后轮到自己
        // 在afterAction中,是优先执行自己的,然后逐步轮向最古老的主人
        $result = $this->afterAction($action, $result);
        foreach ($modules as $module)
        {
            $result = $module->afterAction($action, $result);
        }
    }
    $this->action = $oldAction;
    return $result;
}

7 run

public function run($route, $params = [])
{
    $pos = strpos($route, '/');
    // $route不包含'/',则是action id, 比如index, create, 最后会执行actionIndex, actionCreate
    if ($pos === false)
    {
        return $this->runAction($route, $params);
    }
    // 如果'/'不在开头, 则有主人来执行,比如 site/index, site/create
    elseif ($pos > 0)
    {
        return $this->module->runAction($route, $params);
    }
    // 如果'/'在开头, 比如 /site/index, /site/create, 则由app来处理了
    else
    {
        return Yii::$app->runAction(ltrim($route, '/'), $params);
    }
}

8 findLayoutFile

public function findLayoutFile($view)
{
    $module = $this->module;
    // 如果当前的Controller设置了布局文件, 则直接使用
    if (is_string($this->layout))
    {
        $layout = $this->layout;
    }
    // 如果当前的Controller没有设置布局文件,则往上一直找
    elseif ($this->layout === null)
    {
        while ($module !== null && $module->layout === null)
        {
            $module = $module->module;
        }
        if ($module !== null && is_string($module->layout))
        {
            $layout = $module->layout;
        }
    }

    if (!isset($layout))
    {
        return false;
    }

    // 以@开头的, 会在别名系统中查找真正的布局文件
    if (strncmp($layout, '@', 1) === 0)
    {
        $file = Yii::getAlias($layout);
    }
    // 以/开头的, 则在应用程序的布局文件目录下查找
    // 一般位于views/layouts
    // 新建项目的时候, 这里一般有一个main.php文件
    elseif (strncmp($layout, '/', 1) === 0)
    {
        $file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . substr($layout, 1);
    }
    // 其余情况都在本controller的布局文件目录下查找
    // 比如SiteController
    // 会在views/site/  下查找
    else
    {
        $file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $layout;
    }
    // 该布局文件有返回扩展名, 则返回这个文件
    if (pathinfo($file, PATHINFO_EXTENSION) !== '')
    {
        return $file;
    }
    // 添加php扩展名
    $path = $file . '.' . $view->defaultExtension;
    if ($view->defaultExtension !== 'php' && !is_file($path))
    {
        $path = $file . '.php';
    }

    return $path;
}

9 render, renderContent

public function render($view, $params = [])
{
    // 渲染视图文件
    $content = $this->getView()->render($view, $params, $this);
    // 渲染布局文件
    return $this->renderContent($content);
}
public function renderContent($content)
{
    $layoutFile = $this->findLayoutFile($this->getView());
    if ($layoutFile !== false)
    {
        // render中, 视图渲染的结果通过content传递到布局文件中
        return $this->getView()->renderFile($layoutFile, ['content' => $content], $this);
    }
    else
    {
        return $content;
    }
}

10 参考

1 http://www.cnblogs.com/yiifans/p/3741634.html
以下链接中也有很多好东西:
http://www.yiifans.com/forum.php?mod=viewthread&tid=60

时间: 2025-01-02 08:26:40

Yii2 理解Controller的相关文章

Yii2 理解filters

版本 ActionFilter AccessControl AccessRule VerbFilter 其它 1 版本 // yii\BaseYii\getVersion public static function getVersion() { return '2.0.10'; } 2 ActionFilter 动作过滤器的基类, 有两个重要的变量, 这两个变量都是存储action id $except: 过滤器将不会应用于在这里面出现的action,即使出现在$only中,过滤器也不会有效.

Yii2 理解Component

版本 继承与实现 event和behaviors behaviors 添加Behavior到Component ensureBehaviors attachBehavior和attachBehaviors detachBehavior和detachBehaviors __get __set __call on和off 1 版本 // yii\BaseYii\getVersion public static function getVersion() { return '2.0.10'; } 2

Yii2 理解Validator

版本 Validator BooleanValidator CompareValidator DateValidator DefaultValueValidator EachValidator EmailValidator ExistValidator FileValidator FilterValidator ImageValidator IpValidator NumberValidator RangeValidator RegularExpressionValidator Required

Yii2 理解di

链接 版本 简述 Container Instance TOP 示例说明 TOP 0 链接 http://alex-my.xyz/web/Yii2-理解di 1 版本 // yii\BaseYii\getVersion public static function getVersion() { return '2.0.10'; } 2 简述 简单的说就是di把类的构造函数分析好存储起来,配上给定的参数,创建实例. // 类名给定后通过php的反射机制获取构造函数信息 // set的第二个参数是构

Yii2 理解Object

版本 继承与实现 构造函数 __construct __get __set method_exists __isset __unset 其余判断函数 1 版本 // yii\BaseYii\getVersion public static function getVersion() { return '2.0.10'; } 2 继承与实现 Object实现了Configurable接口. Configureable要求在构造函数的参数末尾加上$config public function __c

Yii2使用驼峰命名的形式访问控制器的示例代码

yii2在使用的时候,访问控制器的时候,如果控制器的名称是驼峰命名法,那访问的url中要改成横线的形式.例如: public function actionRoomUpdate() { // } //访问的时候就要www.test.com/room-update这样访问 最近在做某渠道的直连的时候,他们提供的文档上明确指出接口的形式: 刚开始以为YII2中肯定有这样的设置,然后就去google了下,发现都说不行,自己去看了下,果然,框架里面直接是写死的:(源码)\vendor\yiisoft\y

七天学会ASP.NET MVC (一)——深入理解ASP.NET MVC

系列文章 七天学会ASP.NET MVC (一)--深入理解ASP.NET MVC 七天学会ASP.NET MVC (二)--ASP.NET MVC 数据传递 七天学会ASP.NET MVC (三)--ASP.Net MVC 数据处理 七天学会ASP.NET MVC (四)--用户授权认证问题 七天学会ASP.NET MVC (五)--Layout页面使用和用户角色管理  七天学会ASP.NET MVC (六)--线程问题.异常处理.自定义URL 七天学会ASP.NET MVC(七)--创建单页

ASP.NET MVC 音乐商店 - 3. 视图与模型

转自http://www.cnblogs.com/haogj/archive/2011/11/11/2244895.html 上一篇中有同学提到为什么不使用视图,而使用字符串,这一篇我们就开始使用视图来处理. 我们已经可以从控制器的 Action 中返回一个字符串,这可以帮助我们更好地理解 Controller 是如何工作的.但是对于创建一个 Web 程序来说还是不够的.下面我们使用更好的方法来生成 HTML,主要是通过模板来生成需要的 HTML,这就是视图所要做的.  增加视图模板 为了使用视

深入理解Yii2.0乐观锁与悲观锁的原理与使用

本文介绍了深入理解Yii2.0乐观锁与悲观锁的原理与使用,分享给大家,具体如下: Web应用往往面临多用户环境,这种情况下的并发写入控制, 几乎成为每个开发人员都必须掌握的一项技能. 在并发环境下,有可能会出现脏读(Dirty Read).不可重复读(Unrepeatable Read). 幻读(Phantom Read).更新丢失(Lost update)等情况.具体的表现可以自行搜索. 为了应对这些问题,主流数据库都提供了锁机制,并引入了事务隔离级别的概念. 这里我们都不作解释了,拿这些关键