ThinkPHP框架對URL有一定的規范,所以如果你希望定制你的URL格式的話,就需要好好了解下內置的路由功能了,它能讓你的URL變得更簡潔和有文化。
啟用路由
要使用路由功能,前提是你的URL支持PATH_INFO,并且在項目配置文件中開啟路由:
'URL_ROUTER_ON' => true, //開啟路由
然后就是配置路由規則了,使用
URL_ROUTE_RULES參數進行配置,配置格式是一個數組,每個元素都代表一個路由規則,例如:
'URL_ROUTE_RULES'=>array(
'news/:year/:month/:day' => array('News/archive', 'status=1'),
'news/:id' => 'News/read',
'news/read/:id' => '/news/:1',
),
系統會按定義的順序依次匹配路由規則,一旦匹配到的話,就會定位到路由定義中的模塊(支持分組)和操作方法去執行,并且后面的規則不會繼續匹配。
路由定義
路由規則的定義方式如下:
'路由表達式'=>'路由地址和額外參數'
路由表達式
路由表達式包括規則路由和正則路由的定義表達式,只能使用字符串。
表達式 | 示例 |
---|
正則表達式 | /^blog/(d+)$/ |
規則表達式 | blog/:id |
正則表達式路由表達式支持的正則定義必須以“/”開頭,否則就視為規則表達式。也就是說如果采用
'#^blog/(d+)$#'
方式定義的正則表達式不會被支持,而會被認為是規則表達式進行解析,從而無法正確匹配。
'/^new/(d{4})/(d{2})$/' => 'News/achive?year=:1&month=:2',
對于正則表達式中的每個變量(即正則規則中的子模式)部分,如果需要在后面的路由地址中引用,可以采用:1、:2這樣的方式,序號就是子模式的序號。
更多的關于如何定義正則表達式就不在本文的描述范疇了。
規則表達式3.0的規則路由是從2.1的簡單路由進化而來,雖然無法完美實現正則路由的功能,但是相比簡單路由確實增強了不少,而且比正則路由更方便定義和容易理解。
規則表達式通常包含靜態地址和動態地址,或者兩種地址的結合,例如下面都屬于有效的規則表達式:
'my'=>'Member/myinfo', // 靜態地址路由 類似于之前版本的簡單路由
'blog/:id'=>'Blog/read', // 靜態地址和動態地址結合
'new/:year/:month/:day'=>'News/read', // 靜態地址和動態地址結合
':user/:blog_id'=>'Blog/read',// 全動態地址
規則表達式的定義以“/”為參數分割符(無論你的URL_PATHINFO_DEPR設置是什么,請確保在定義規則表達式的時候統一使用“/”進行URL參數分割)。
每個參數中以“:”開頭的參數都表示動態參數,并且會自動對應一個GET參數,例如:id表示該處匹配到的參數可以使用$_GET['id']方式獲取,:year :month :day 則分別對應$_GET['year'] $_GET['month'] $_GET['day']。
數字約束支持對變量的類型檢測,但僅僅支持數字類型的約束定義,例如
'blog/:idd'=>'Blog/read',
表示只會匹配數字參數,如果你需要更加多的變量類型檢測,請使用正則表達式定義來解決。
規則排除非數字變量支持簡單的排除功能,主要是起到避免解析混淆的作用,例如:
'news/:cate^add|edit|delete'=>'News/category'
因為規則定義的局限性,恰巧我們的路由規則里面的news和實際的news模塊是相同的命名,而:cate并不能自動區分當前URL里面的動態參數是實際的操作名還是路由變量,所以為了避免混淆,我們需要對路由變量cate進行一些排除以幫助我們進行更精確的路由匹配,格式^add|edit|delete表示,匹配除了add edit 和delete之外的所有字符串,我們建議更好的方式還是改進你的路由規則,避免路由規則和模塊同名的情況存在,例如
'new/:cate'=>'News/category'
就可以更簡單的定義路由規則了。
完全匹配規則匹配檢測的時候只是對URL從頭開始匹配,只要URL地址包含了定義的路由規則就會匹配成功,如果希望完全匹配,可以使用$符號,例如:
'new/:cate$'=> 'News/category',
http://serverName/index.php/new/info
會匹配成功
而
http://serverName/index.php/new/info/2
則不會匹配成功
如果是采用
'new/:cate'=> 'News/category',
方式定義的話,則兩種方式的URL訪問都可以匹配成功。
路由地址和額外參數
路由地址和額外參數表示前面的路由表達式最終需要路由到的地址并且允許隱式傳入URL里面沒有的一些參數,這里允許使用字符串或者數組方式定義,支持下面5種方式定義:
定義方式 | 定義格式 |
---|
方式1:路由到內部地址(字符串) | '[分組/模塊/操作]?額外參數1=值1&額外參數2=值2...' |
方式2:路由到內部地址(數組)參數采用字符串方式 | array('[分組/模塊/操作]','額外參數1=值1&額外參數2=值2...') |
方式3:路由到內部地址(數組)參數采用數組方式 | array('[分組/模塊/操作]',array('額外參數1'=>'值1','額外參數2'=>'值2'...)) |
方式4:路由到外部地址(字符串)301重定向 | '外部地址' |
方式5:路由到外部地址(數組)可以指定重定向代碼 | array('外部地址','重定向代碼') |
如果路由地址以“/”或者“http”開頭則會認為是一個重定向地址或者外部地址,例如:
'blog/:id'=>'/blog/read/id/:1'
和
'blog/:id'=>'blog/read/'
雖然都是路由到同一個地址,但是前者采用的是301重定向的方式路由跳轉,這種方式的好處是URL可以比較隨意(包括可以在URL里面傳入更多的非標準格式的參數),而后者只是支持模塊和操作地址。舉個例子,如果我們希望avatar/123重定向到
/member/avatar/id/123_small的話,只能使用:
'avatar/:id'=>'/member/avatar/id/:1_small'
路由地址采用重定向地址的話,如果要引用動態變量,也是采用:1、:2 的方式。
采用重定向到外部地址通常對網站改版后的URL遷移過程非常有用,例如:
'blog/:id'=>'http://blog.thinkphp.cn/read/:1'
表示當前網站(可能是http://thinkphp.cn)的 blog/123地址會直接重定向到 http://blog.thinkphp.cn/read/123。
在路由跳轉的時候支持額外傳入參數對(額外參數指的是不在URL里面的參數,隱式傳入需要的操作中,有時候能夠起到一定的安全防護作用,后面我們會提到),支持“額外參數1=值1&額外參數2=值2”或者array('額外參數1'=>'值1','額外參數2'=>'值2'...)這樣的寫法,可以參考不同的定義方式選擇。例如:
'blog/:id'=>'blog/read/?status=1&app_id=5',
'blog/:id'=>array('blog/read/?status=1&app_id=5'),
'blog/:id'=>array('blog/read/','status=1&app_id=5'),
'blog/:id'=>array('blog/read/',array('status'=>1,'app_id'=>5)),
上面的路由規則定義中額外參數的傳值方式都是等效的。status和app_id參數都是URL里面不存在的,屬于隱式傳值,當然并不一定需要用到,只是在需要的時候可以使用。
實例說明
通過上面的講解,我們了解了如何定義路由規則,下面我們來舉個例子加深印象。
假設我們定義了News控制器如下(代碼實現僅供參考):
class NewsAction extends Action{
public function read(){
$New = M('New');
if(isset($_GET['id'])) {
// 根據id查詢結果
$data = $New->find($_GET['id']);
}elseif(isset($_GET['name'])){
// 根據name查詢結果
$data = $New->getByName($_GET['name']);
}
$this->data = $data;
$this->display();
}
public function archive(){
$New = M('New');
$year = $_GET['year'];
$month = $_GET['month'];
$begin_time = strtotime($year . $month . "01");
$end_time = strtotime("+1 month", $begin_time);
$map['create_time'] = array(array('gt',$begin_time),array('lt',$end_time));
$map['status'] = 1;
$list = $New->where($map)->select();
$this->list = $list;
$this->display();
}
}
定義路由規則如下:
'URL_ROUTE_RULES' => array( //定義路由規則
'new/:idd' => 'News/read',
'new/:name' => 'News/read',
'new/:yeard/:monthd' => 'News/archive',
),
然后,我們訪問:
http://serverName/index.php/new/8
會匹配到第一個路由規則,實際執行的效果等效于訪問:
http://serverName/index.php/News/read/id/8
當訪問:
http://serverName/index.php/new/hello
會匹配到第二個路由規則,實際執行的效果等效于訪問:
http://serverName/index.php/News/read/name/hello
那么如果訪問:
http://serverName/index.php/new/2012/03
是否會匹配第三個路由規則呢?我們期望的實際執行的效果能夠等效于訪問:
http://serverName/index.php/News/archive/year/2012/month/03
事實上卻沒有,因為http://serverName/index.php/new/2012/這個URL在進行路由匹配過程中已經優先匹配到了第一個路由規則了,把2012當成id的值傳入了,這種情況屬于路由規則的沖突,解決辦法有兩個:
1、調整定義順序路由定義改成:
'URL_ROUTE_RULES' => array( //定義路由規則
'new/:yeard/:monthd' => 'News/archive',
'new/:idd' => 'News/read',
'new/:name' => 'News/read',
),
接下來,當我們再次訪問:
http://serverName/index.php/new/2012/03
的時候,達到了預期的訪問效果。所以如果存在可能規則沖突的情況,盡量把規則復雜的規則定義放到前面,確保最復雜的規則可以優先匹配到。但是如果路由規則定義多了之后,仍然很容易混淆,所以需要尋找更好的解決辦法。
2、利用完全匹配功能現在我們來利用路由的完全匹配定義功能,把路由定義改成:
'URL_ROUTE_RULES' => array( //定義路由規則
'new/:idd$' => 'News/read',
'new/:name$' => 'News/read',
'new/:yeard/:monthd$' => 'News/archive',
),
在規則最后加上$符號之后,表示完整匹配當前的路由規則,就可以避免規則定義的沖突了。對于規則路由來說,簡單的理解就是URL里面的參數數量或者類型約束要完全一致。
所以,如果我們訪問
http://serverName/index.php/new/2012/03/01
的話,是不會匹配成功任何一條路由的。
3、利用正則路由當然,解決問題的辦法總是不止一種,對于復雜的情況,我們不要忘了使用正則路由規則定義,在你找不到解決方案的時候,正則路由總能幫到你。
要實現上面的同樣路由功能的話,還可以用下面的規則定義:
'URL_ROUTE_RULES' => array( //定義路由規則
'/^new/(d+)$/' => 'News/read?id=:1',
'/^new/(w+)$/' => 'News/read?name=:1',
'/^new/(d{4})/(d{2})$/' => 'News/achive?year=:1&month=:2',
),