Yii2 理解Controller

简介: 版本继承与实现actionscreateActiongetModulesrunActionrunfindLayoutFilerender renderContent参考1 版本// yii\BaseYii\getVersionpublic static function getVersion(){ return

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

相关文章
|
6月前
|
JavaScript Java 数据库连接
代码生成器(entity、dao、service、controller、vue)
减少你最基础的crud开发,减少你开发的基础工作
67 1
|
缓存 安全 数据处理
Yii2相对于Yii1有哪些改进?
Yii2相对于Yii1有哪些改进?
|
Oracle 关系型数据库 MySQL
Yii2的基本要求是什么?
Yii2的基本要求是什么?
119 0
|
缓存 开发框架 安全
Yii2是什么?
Yii2是什么?
247 0
|
Java 开发者 Spring
controller 配置总结 | 学习笔记
快速学习 controller 配置总结。
|
Java Spring
SpringMVC【开发Controller】详解(三)
本文主要是讲解在Controller中的开发
166 0
SpringMVC【开发Controller】详解(三)
|
前端开发 Java Spring
|
JSON 缓存 数据格式
Beego Controller
controller 逻辑 controller就是处理具体的逻辑的,router将请求分发到指定的controlller,controller处理请求,然后返回。
1785 0
|
PHP Windows 前端开发
Yii 初识
接管一个Yii的系统,因为没有文档,所以非常上火。 01 查版本 Yii::getVersion(); 02 生成webapp Yii 是支持通过命令行生成webapp的。其中, yiic.bat是Windows 下的 yiic 命令行脚本。
1219 0