资讯专栏INFORMATION COLUMN

CI3设置子目录控制器为默认控制器的解决办法

niceforbear / 477人阅读

摘要:在框架中配置文件多目录前后台应该是个很常见的事情。于是在求学问道的途中,终于得到了比较完美的解决方法。业务需求环境需求在中实现前后台的效果。因为已经不支持设置子目录下的控制器为默认控制器的功能。


    在框架中配置文件多目录前后台应该是个很常见的事情。像一般的php框架(CITp等)采用都是单一入口模式,或许有人会直接在框架根目录新建文件admin.php,然后改变框架app结构,以达到访问不同入口文件名获得不同资源的效果。那么在CI中一样可以这样做,不过个人觉得这种方法太浪费资源(占用了几十k的资源吧)。于是在‘求学问道’的途中,终于得到了比较完美的解决方法。

业务需求

环境:codeigniter 3
需求:在CI3中实现前后台的效果。例:

地址栏输入xxx.com默认访问前台主页,输入xxx.com/admin访问后台

所遇问题

依照惯例,我们会在框架中的config/route.php路由配置文件中配置我们的前后台访问路径:

// path => application/config/route.php

$route["admin"] = "admin/admin"; //后台路径
$route["default_controller"] = "home/home"; // 默认前台路径
$route["404_override"] = "";
$route["translate_uri_dashes"] = FALSE;

一般来说,我们这样配置是没问题的,但是有一个条件就是在CI3以下的版本中是没任何问题。但是目前的框架版本CI3,所以就会出现找不到资源文件的情况,空口无凭不算,下面是两张CI2CI3的route的配置图,和浏览器效果图:

CI2和CI3相同路由配置对比图:

CI2和CI3相同路由配置运行网页对比图:

由上述两图可以看到,相同的路由配置下,但是结果却是不一样。因为CI3已经不支持设置子目录下的控制器为默认控制器的功能。但是要完成需求描述,这样的效果该如何实现呢?接下来看我们追踪CI3源码;

源码追踪core/Route.php

通过上面的结论,我们应该可以联想到出现404这样的报错,应该是解析default_controller的时候出现的问题,于是我在sublime中利用全文检索查询哪里有用到default_controller,搜索的范围可以假定为在CI核心目录中,因为路由的解析一般是由核心目录里的路由类完成的,于是查询范围锁定在system目录,得出下面的结果:

锁定_set_default_controller方法

于是我一步一步排查,最终发现是这一段代码的问题:

// Is the method being specified?
if (sscanf($this->default_controller, "%[^/]/%s", $class, $method) !== 2)
{
    $method = "index";
}

if ( ! file_exists(APPPATH."controllers/".$this->directory.ucfirst($class).".php"))
{
    // This will trigger 404 later
    return;
}

上面的代码第一段if里面拆分我们在config/route.php里配置好的default_controller值得到控制器名并赋值给变量class方法名并赋值给method,如果method为空则默认为index,很显然这与我们的初衷不相符,因为我们的计划是default_controller里的值home/home,第一个home是目录名(floder_name),第二个home才是控制器名字(controller_name)

而第二段if的意思是判断控制器文件是否存在,排查也发现控制器名竟然不存在,打印

APPPATH . "controllers/" . $this->directory . ucfirst($class) . ".php"

得到:E:WWWci3applicationcontrollers/Home.php,这显然与我们的实际目录不相符,我们的实际目录应该是E:WWWci3applicationcontrollers/home/Home.php,锁定这两个问题之后,就可以思考如何修正这里了,刚开始对这个地方的改动想法是这样的:

假定设置默认的控制器值为Home/home/index(目录名/控制器名/方法名)的形式

修改core/Route.php源码中的_set_default_controller方法,截取default_controller的值进行处理

修改_set_default_controller方法如下:

protected function _set_default_controller()
{
    if (empty($this->default_controller))
    {
        show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file.");
    }

    /**
     * if里为自己修改的部分
     * 1.截取default_controller为数组
     * 2.如果default_controller_arr大于3 表示是默认控制器过来的
     * 3.赋值相应的变量
     */
    $default_controller_arr = explode("/", $this->default_controller);
    if(count($default_controller_arr) == 3) {
        // 赋值控制器所在目录
        $this->directory = trim($default_controller_arr[0], "/") . "/";
        // 赋值控制器名
        $class = $default_controller_arr[1];
        // 因为这里计划约定默认控制器输入完整uri 即目录名/控制器名/方法名的形式
        // 所以方法名这里一定不为空
        $method = $default_controller_arr[2];

    }else {
        // Is the method being specified?
        if (sscanf($this->default_controller, "%[^/]/%s", $class, $method) !== 2)
        {
            $method = "index";
        }
    }
    if ( ! file_exists(APPPATH."controllers/".$this->directory.ucfirst($class).".php"))
    {
        // This will trigger 404 later
        return;
    }

    $this->set_class($class);
    $this->set_method($method);

    // Assign routed segments, index starting from 1
    $this->uri->rsegments = array(
        1 => $class,
        2 => $method
    );

    log_message("debug", "No URI present. Default controller set.");
}

虽说这样修改测试成功了,但是觉得并不是最好的解决办法(修改源码一般是最后的解决手段),于是求助codeigniter中国的官方微信群的小伙伴,在群里和Hex(手动@Hex)老大讨论了一下这个功能的解决方案,最终在他的帮助下得到了比较完美的解决方法,就是要在application/core里新建一个自己的扩展路由类MY_Router.php,然后定义自己的_set_default_controller方法,代码如下,顺便贴上自己上面设想的解决方法:

default_controller)) {
            
            show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file.");
        }

        if (sscanf($this->default_controller, "%[^/]/%s", $class, $method) !== 2) {
            $method = "index";
        }

        /**
         * 1.判断目录是否存在
         * 2.如果存在 调用设置控制器目录方法 详细参考system/core/Route.php set_directory方法
         * 3.接着再把method拆分 赋值给$class $method $method为空则设置为index
         */
        if( is_dir(APPPATH."controllers/".$class) ) {
             
            // Set the class as the directory
            $this->set_directory($class);
            
            // $method is the class
            $class = $method;
            
            // Re check for slash if method has been set
            if (sscanf($method, "%[^/]/%s", $class, $method) !== 2) {
                $method = "index";
            }
        }

        if ( ! file_exists(APPPATH."controllers/".$this->directory.ucfirst($class).".php")) {
            // This will trigger 404 later
            return;
        }

        $this->set_class($class);
        $this->set_method($method);

        // Assign routed segments, index starting from 1
        $this->uri->rsegments = array(
            1 => $class,
            2 => $method
        );
        log_message("debug", "No URI present. Default controller set.");
    }

    /**
     * @author 命中水、 
     * @date(2017-8-7)
     * 
     * 使用这个方法时 把这个方法名和上面的方法名调换一下 
     * application/config/route.php default_controller的值写uri全称(目录名/控制器名/方法名) 即可
     * 因为最终Route.php路由类库调用的还是_set_default_controller方法
     */
    protected function _set_default_controller_me() {
         
        if (empty($this->default_controller))
        {
            show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file.");
        }

        /**
         * if里为自己修改的部分
         * 1.截取default_controller为数组
         * 2.如果default_controller_arr大于3 表示是默认控制器过来的
         * 3.赋值相应的变量
         */
        $default_controller_arr = explode("/", $this->default_controller);
        if(count($default_controller_arr) == 3) {
            // 赋值控制器目录
            $this->directory = trim($default_controller_arr[0], "/") . "/";
            // 赋值控制器名
            $class  = $default_controller_arr[1];
            // 因为这里计划约定默认控制器输入完整uri 即目录名/控制器名/方法名的形式
            // 所以方法名这里一定不为空
            $method = $default_controller_arr[2];

        }else {
            if (sscanf($this->default_controller, "%[^/]/%s", $class, $method) !== 2)
            {
                $method = "index";
            }
        }
        if ( ! file_exists(APPPATH."controllers/".$this->directory.ucfirst($class).".php"))
        {
            // This will trigger 404 later
            return;
        }

        $this->set_class($class);
        $this->set_method($method);

        // Assign routed segments, index starting from 1
        $this->uri->rsegments = array(
            1 => $class,
            2 => $method
        );

        log_message("debug", "No URI present. Default controller set.");
    }

}

以上代码比较完美的那个,亲测有效!!!(自己的这个简单测试了一下,也可以使用)

资源 参考文章

How to select default controller in subfolder?

How to use a sub folder in default controller route in CodeIgniter 3

资源

MY_Route.php

CodeIgniter中国微信群

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/25585.html

相关文章

  • 视频直播常见问题与解决办法汇总【系列二—直播截图】

    摘要:常见问题直播截图是针对于推流的为粒度设置的,用户可以设置某个下的所有的截图规则。 摘要: 使用场景 用户常需要对视频直播的内容进行分析。例如,需要对直播中的某一帧的内容作为封面图片或者对于直播内容进行鉴黄以查看内容合法性等需求。因此,视频直播提供了实时截图功能满足用户的截图需求。 截图功能介绍 截图功能配置 配置视频直播的截图功能可以通过两种方法: 通过控制台创建:详情参考 控制台配置...

    piapia 评论0 收藏0
  • 转载:浏览器常见兼容问题以及解决办法

    摘要:浏览器兼容问题四行内属性标签,设置后采用布局,又有横行的的情况,间距问题症状里的间距比超过设置的间距碰到几率解决方案在后面加入备注行内属性标签,为了设置宽高,我们需要设置除了标签比较特殊。 (1)浏览器兼容问题一:不同浏览器的标签默认的外补丁和内补丁不同问题症状:随便写几个标签,不加样式控制的情况下,各自的margin 和padding差异较大。碰到频率:100%解决方案:CSS里 *...

    AaronYuan 评论0 收藏0

发表评论

0条评论

niceforbear

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<