2019獨角獸企業重金招聘Python工程師標準>>>
在Codeiniter(以下統稱CI) 2.X版本中,我們就通過拓展核心類庫實現了HMVC,但是同樣的代碼,拿到CI 3中,就很有可能不好用了。
###拓展核心類庫方式
官方的文檔對拓展核心類有詳細的說明:
你定義的類必須繼承自父類。 你的類名和文件名必須以 MY_ 開頭。(這是可配置的,見下文) 舉個例子,要擴展原始的 Input 類,你需要新建一個文件 application/core/MY_Input.php,然后像下面這樣定義你的類:
class MY_Input extends CI_Input {}
CI 控制器加載的過程很簡單,官方文檔有圖如下:
我們可以看到,在控制器開始加載看,CI是做了Routing(路由)和Security(安全)的操作的,所以,我們需要重寫,或者說,在CI拓展我們想要的功能,比如:HMVC
###2.0中擴展
在2.0版本中,筆者曾適用過Jens Segers開源的HMVC模塊,代碼的實現就是對Routee和Loader進行了重寫。 Jens Segers的主頁 核心的代碼如下:
if (is_dir($source = $location . $module . '/controllers/')) {$this->module = $module;$this->directory = $relative . $module . '/controllers/';// 根目錄下的模塊?if ($directory && is_file($source . $directory . '.php')) {$this->class = $directory;return array_slice($segments, 1);}// 子模塊?if ($directory && is_dir($source . $directory . '/')) {$source = $source . $directory . '/';$this->directory .= $directory . '/';// 子控制器?if (is_file($source . $directory . '.php')) {return array_slice($segments, 1);}// 子文件夾包含有默認控制器?if (is_file($source . $this->default_controller . '.php')) {$segments[1] = $this->default_controller;return array_slice($segments, 1);}// 子文件夾中的控制器? if ($controller && is_file($source . $controller . '.php')) {return array_slice($segments, 2);}}// 控制器和文件夾名一樣?if (is_file($source . $module . '.php')) {return $segments;}// 適用默認的控制器?if (is_file($source . $this->default_controller . '.php')) {$segments[0] = $this->default_controller;return $segments;}}
很簡單的拓展 就能實現HMVC模式了,同樣的,還得重寫Loader中的加載器,不然會找不到文件。
###CI 3 HMVC拓展 到了CI3中,上述方法已經不好用了,CI 3 對路由有了更多的考慮,在初始化路由時,就進行了解析。假設寫MY_Ruter類,必須要重寫3個方法: CI 2 Router構造
function __construct(){$this->config =& load_class('Config', 'core');$this->uri =& load_class('URI', 'core');log_message('debug', "Router Class Initialized");}
CI 3 Router構造
public function __construct($routing = NULL){$this->config =& load_class('Config', 'core');$this->uri =& load_class('URI', 'core');$this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE);is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']);$this->_set_routing();if (is_array($routing)){empty($routing['controller']) OR $this->set_class($routing['controller']);empty($routing['function']) OR $this->set_method($routing['function']);}log_message('info', 'Router Class Initialized');}
可以看到,在CI3的構造方法中就已經對URL進行解析,方法的調用過程為: _set_routing() -> _validate_request() -> _parse_routes() -> _set_request() 那我們為了要實現HMVC,這幾個方法是必然要按照我們自己的方法實現的。 _validate_request 中 我們加入部分驗證,即可達到簡單的HMVC
if (is_dir($source = $relative . $module . '/controllers/')) {$this->module = $module;$this->directory = '../'.$location.$module . '/controllers/';// 如果 有 application/$module/controollers/$directory.php 文件if ($directory && is_file($source . ucfirst($directory) . '.php')) {return array_slice($segments, 1);}//如果application/$module/$directory 是一個文件夾if ($directory && is_dir($source . $directory . '/')) {$source = $source . $directory . '/';$this->directory .= $directory . '/';// index.php/$modules/$directory/$controller//如果包含 控制器 $controllerif ($controller && is_file($source . ucfirst($controller) . '.php')) {return array_slice($segments, 2);}//如果有默認控制器if (is_file($source . $this->default_controller . '.php')) {$segments[1] = $this->default_controller;return array_slice($segments, 1);}//如果有 application/$module/$directory.phpif (is_file($source . $directory . '.php')) {return array_slice($segments, 1);}}//如果有 application/$module/$module.php if (is_file($source . $module . '.php')) {return $segments;}// 默認控制器if (is_file($source . $this->default_controller . '.php')) {$segments[0] = $this->default_controller;return $segments;}}
為了讓CI_ROUTER知道我們模塊拓展的位置,我們在配置文件中加入選項,并在CI_ROUTER的構造器中加入如下代碼:
$locations = $this->config->item('modules_locations');if (!$locations) {$locations = array('modules/');} else if (!is_array($locations)) {$locations = array($locations);}$this->config->set_item('modules_locations', $locations);
操作完以上步驟,就可以實現大部分HMVC的拓展了。 本文代碼:https://git.oschina.net/liwenlong/Codeigniter-3-HMVC.git 說明:CI3中對控制器大小寫由嚴格的控制了,為了符合CI3的一貫規則,所以我們使用了ucfirst()方式尋找首字母大寫的類名,一定要注意。