1. 程式人生 > >CI3設定子目錄控制器為預設控制器的解決辦法

CI3設定子目錄控制器為預設控制器的解決辦法

 在框架中配置檔案多目錄前後臺應該是個很常見的事情。像一般的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,所以就會出現找不到資原始檔的情況,空口無憑不算,下面是兩張CI2

CI3的route的配置圖,和瀏覽器效果圖:

CI2和CI3相同路由配置對比圖:

CI2和CI3路由配置對

CI2和CI3相同路由配置執行網頁對比圖:

CI2和CI3網頁展示對比

由上述兩圖可以看到,相同的路由配置下,但是結果卻是不一樣。因為CI3已經不支援設定子目錄下的控制器為預設控制器的功能。但是要完成需求描述,這樣的效果該如何實現呢?接下來看我們追蹤CI3原始碼;

原始碼追蹤core/Route.php

通過上面的結論,我們應該可以聯想到出現404這樣的報錯,應該是解析default_controller的時候出現的問題,於是我在sublime中利用全文檢索查詢哪裡有用到default_controller,搜尋的範圍可以假定為在CI

核心目錄中,因為路由的解析一般是由核心目錄裡的路由類完成的,於是查詢範圍鎖定在system目錄,得出下面的結果:

查詢結果

鎖定_set_default_controller方法

Route類中_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:\WWW\ci3\application\controllers/Home.php,這顯然與我們的實際目錄不相符,我們的實際目錄應該是E:\WWW\ci3\application\controllers/home/Home.php,鎖定這兩個問題之後,就可以思考如何修正這裡了,剛開始對這個地方的改動想法是這樣的:

  1. 假定設定預設的控制器值為Home/home/index(目錄名/控制器名/方法名)的形式
  2. 修改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方法,程式碼如下,順便貼上自己上面設想的解決方法:

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class MY_Router extends CI_Router {

    /**
     * 個人認為比較完美解決問題的方法
     */
    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 (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.');
    }

}

以上程式碼比較完美的那個,親測有效!!!(自己的這個簡單測試了一下,也可以使用)

資源

參考文章

  1. How to select default controller in subfolder?
  2. How to use a sub folder in default controller route in CodeIgniter 3

資源

  1. MY_Route.php
  2. CodeIgniter中國微信群
  •  

贊  |   0收藏  |  1
讚賞支援

如果覺得我的文章對你有用,請隨意讚賞

 

你可能感興趣的

1 條評論

預設排序時間排序

汝為誰容 · 9月16日

寫到後面爛尾了。
home/home這在 資料夾/控制器方式 只需要在自定義的MY_Router裡

class MY_Router extends CI_Router {

/**
 * 個人認為比較完美解決問題的方法
 */
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) == 2) {
        // 賦值控制器目錄
        $this->directory = trim($default_controller_arr[0], '/') . '/';
        // 賦值控制器名
        $class  = $default_controller_arr[1];
        // 因為這裡計劃約定預設控制器輸入完整uri 即目錄名/控制器名/方法名的形式
        $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.');
}

}

這樣寫就行,你在最後給的方法是home/home/index這種設定