CI3設定子目錄控制器為預設控制器的解決辦法
在框架中配置檔案多目錄
、前後臺
應該是個很常見的事情。像一般的php框架(CI
、Tp
等)採用都是單一入口模式,或許有人會直接在框架根目錄新建檔案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相同路由配置執行網頁對比圖:
由上述兩圖可以看到,相同的路由配置下,但是結果卻是不一樣。因為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:\WWW\ci3\application\controllers/Home.php
,這顯然與我們的實際目錄不相符,我們的實際目錄應該是E:\WWW\ci3\application\controllers/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
方法,程式碼如下,順便貼上自己上面設想的解決方法:
<?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.');
}
}
以上程式碼比較完美的那個,親測有效!!!(自己的這個簡單測試了一下,也可以使用)
資源
參考文章
- How to select default controller in subfolder?
- How to use a sub folder in default controller route in CodeIgniter 3
資源
贊 | 0收藏 | 1
讚賞支援
如果覺得我的文章對你有用,請隨意讚賞
你可能感興趣的
- PHP_ThinkPHPaLogythinkphpphp框架php
- 五分鐘入門 Dingo APICrazyCodesdingolaravelphp
- API後端框架Godtail2-路由元件godtailphp
- coding.net演示平臺獲取mysql服務連線資訊變數aapuwordpressphpcloud-foundrycoding.net
- 關於專案中圖片快取的問題Basuphp
- 框架設計模式Dyanphp
- 【原創】Laravel5.5及以上版本多環境.env配置讀取leeonlaravelphp
- 使用SAE和Gitcafe開發網站應用P_Chou水冗phpgitcafesina-app-engine
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這種設定