欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Yii2 理解Controller

程序员文章站 2024-02-14 12:52:10
...

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