ThinkPHP 路由

ThinkPHP框架對URL有一定的規範,所以如果你希望定製你的URL格式的話,就需要好好了解下內置的路由功能了,它能讓你的URL變得更簡潔和有文化。

啓用路由

要使用路由功能,前提是你的URL支持PATH_INFO,並且在項目配置文件中開啓路由:
  1. 'URL_ROUTER_ON'   => true, //開啓路由
複製代碼
然後就是配置路由規則了,使用URL_ROUTE_RULES參數進行配置,配置格式是一個數組,每個元素都代表一個路由規則,例如:
  1. 'URL_ROUTE_RULES'=>array(
  2.     'news/:year/:month/:day' => array('News/archive', 'status=1'),
  3.     'news/:id'               => 'News/read',
  4.     'news/read/:id'          => '/news/:1',
  5.  ),
複製代碼
系統會按定義的順序依次匹配路由規則,一旦匹配到的話,就會定位到路由定義中的模塊(支持分組)和操作方法去執行,並且後面的規則不會繼續匹配。

路由定義

路由規則的定義方式如下:
  1. '路由表達式'=>'路由地址和額外參數'
複製代碼

路由表達式

路由表達式包括規則路由和正則路由的定義表達式,只能使用字符串。
表達式 示例
正則表達式 /^blog\/(\d+)$/
規則表達式 blog/:id
正則表達式
路由表達式支持的正則定義必須以“/”開頭,否則就視爲規則表達式。也就是說如果採用
  1. '#^blog\/(\d+)$#'
複製代碼
方式定義的正則表達式不會被支持,而會被認爲是規則表達式進行解析,從而無法正確匹配。
  1. '/^new\/(\d{4})\/(\d{2})$/' => 'News/achive?year=:1&month=:2',
複製代碼
對於正則表達式中的每個變量(即正則規則中的子模式)部分,如果需要在後面的路由地址中引用,可以採用:1、:2這樣的方式,序號就是子模式的序號。
更多的關於如何定義正則表達式就不在本文的描述範疇了。

規則表達式
3.0的規則路由是從2.1的簡單路由進化而來,雖然無法完美實現正則路由的功能,但是相比簡單路由確實增強了不少,而且比正則路由更方便定義和容易理解。
規則表達式通常包含靜態地址和動態地址,或者兩種地址的結合,例如下面都屬於有效的規則表達式:
  1. 'my'=>'Member/myinfo', // 靜態地址路由 類似於之前版本的簡單路由
  2.  'blog/:id'=>'Blog/read', // 靜態地址和動態地址結合
  3.  'new/:year/:month/:day'=>'News/read', // 靜態地址和動態地址結合
  4.  ':user/:blog_id'=>'Blog/read',// 全動態地址
複製代碼
規則表達式的定義以“/”爲參數分割符(無論你的URL_PATHINFO_DEPR設置是什麼,請確保在定義規則表達式的時候統一使用“/”進行URL參數分割)。
每個參數中以“:”開頭的參數都表示動態參數,並且會自動對應一個GET參數,例如:id表示該處匹配到的參數可以使用$_GET['id']方式獲取,:year :month :day 則分別對應$_GET['year'] $_GET['month'] $_GET['day']。

數字約束
支持對變量的類型檢測,但僅僅支持數字類型的約束定義,例如
  1. 'blog/:id\d'=>'Blog/read',
複製代碼
表示只會匹配數字參數,如果你需要更加多的變量類型檢測,請使用正則表達式定義來解決。

規則排除
非數字變量支持簡單的排除功能,主要是起到避免解析混淆的作用,例如:
  1. 'news/:cate^add|edit|delete'=>'News/category'
複製代碼
因爲規則定義的侷限性,恰巧我們的路由規則裏面的news和實際的news模塊是相同的命名,而:cate並不能自動區分當前URL裏面的動態參數是實際的操作名還是路由變量,所以爲了避免混淆,我們需要對路由變量cate進行一些排除以幫助我們進行更精確的路由匹配,格式^add|edit|delete表示,匹配除了add edit 和delete之外的所有字符串,我們建議更好的方式還是改進你的路由規則,避免路由規則和模塊同名的情況存在,例如
  1. 'new/:cate'=>'News/category'
複製代碼
就可以更簡單的定義路由規則了。

完全匹配
規則匹配檢測的時候只是對URL從頭開始匹配,只要URL地址包含了定義的路由規則就會匹配成功,如果希望完全匹配,可以使用$符號,例如:
  1. 'new/:cate$'=> 'News/category',
複製代碼
  1. http://serverName/index.php/new/info
複製代碼
會匹配成功
  1. http://serverName/index.php/new/info/2 
複製代碼
則不會匹配成功
如果是採用
  1. '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”開頭則會認爲是一個重定向地址或者外部地址,例如:
  1. 'blog/:id'=>'/blog/read/id/:1'
複製代碼
  1. 'blog/:id'=>'blog/read/'
複製代碼
雖然都是路由到同一個地址,但是前者採用的是301重定向的方式路由跳轉,這種方式的好處是URL可以比較隨意(包括可以在URL裏面傳入更多的非標準格式的參數),而後者只是支持模塊和操作地址。舉個例子,如果我們希望avatar/123重定向到
/member/avatar/id/123_small的話,只能使用:
  1. 'avatar/:id'=>'/member/avatar/id/:1_small'
複製代碼
路由地址採用重定向地址的話,如果要引用動態變量,也是採用:1、:2 的方式。

採用重定向到外部地址通常對網站改版後的URL遷移過程非常有用,例如:
  1. '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'...)這樣的寫法,可以參考不同的定義方式選擇。例如:
  1. 'blog/:id'=>'blog/read/?status=1&app_id=5',
  2.  'blog/:id'=>array('blog/read/?status=1&app_id=5'),
  3.  'blog/:id'=>array('blog/read/','status=1&app_id=5'),
  4.  'blog/:id'=>array('blog/read/',array('status'=>1,'app_id'=>5)),
複製代碼
上面的路由規則定義中額外參數的傳值方式都是等效的。status和app_id參數都是URL裏面不存在的,屬於隱式傳值,當然並不一定需要用到,只是在需要的時候可以使用。

實例說明

通過上面的講解,我們瞭解瞭如何定義路由規則,下面我們來舉個例子加深印象。
假設我們定義了News控制器如下(代碼實現僅供參考):
  1. class NewsAction extends Action{
  2.     public function read(){
  3.         $New = M('New');
  4.         if(isset($_GET['id'])) {
  5.             // 根據id查詢結果
  6.             $data = $New->find($_GET['id']);
  7.         }elseif(isset($_GET['name'])){
  8.             // 根據name查詢結果
  9.             $data = $New->getByName($_GET['name']);
  10.         }
  11.         $this->data = $data;
  12.         $this->display();
  13.     }
  14.     public function archive(){
  15.         $New = M('New');
  16.         $year   =   $_GET['year'];
  17.         $month  =   $_GET['month'];
  18.         $begin_time = strtotime($year . $month . "01");
  19.         $end_time = strtotime("+1 month", $begin_time);
  20.         $map['create_time'] =  array(array('gt',$begin_time),array('lt',$end_time));
  21.         $map['status']  =   1;
  22.         $list = $New->where($map)->select();
  23.         $this->list =   $list;
  24.         $this->display();
  25.     }
  26.  }
複製代碼
定義路由規則如下:
  1. 'URL_ROUTE_RULES' => array( //定義路由規則 
  2.     'new/:id\d'    => 'News/read',
  3.     'new/:name'    => 'News/read',
  4.     'new/:year\d/:month\d'  => 'News/archive',
  5.  ),
複製代碼
然後,我們訪問:
  1. http://serverName/index.php/new/8
複製代碼
會匹配到第一個路由規則,實際執行的效果等效於訪問:
  1. http://serverName/index.php/News/read/id/8
複製代碼
當訪問:
  1. http://serverName/index.php/new/hello
複製代碼
會匹配到第二個路由規則,實際執行的效果等效於訪問:
  1. http://serverName/index.php/News/read/name/hello
複製代碼
那麼如果訪問:
  1. http://serverName/index.php/new/2012/03
複製代碼
是否會匹配第三個路由規則呢?我們期望的實際執行的效果能夠等效於訪問:
  1. http://serverName/index.php/News/archive/year/2012/month/03
複製代碼
事實上卻沒有,因爲http://serverName/index.php/new/2012/這個URL在進行路由匹配過程中已經優先匹配到了第一個路由規則了,把2012當成id的值傳入了,這種情況屬於路由規則的衝突,解決辦法有兩個:
1、調整定義順序
路由定義改成:
  1. 'URL_ROUTE_RULES' => array( //定義路由規則
  2.     'new/:year\d/:month\d'  => 'News/archive',
  3.     'new/:id\d'                    => 'News/read',
  4.     'new/:name'    => 'News/read',
  5.  ),
複製代碼
接下來,當我們再次訪問:
  1. http://serverName/index.php/new/2012/03
複製代碼
的時候,達到了預期的訪問效果。所以如果存在可能規則衝突的情況,儘量把規則複雜的規則定義放到前面,確保最複雜的規則可以優先匹配到。但是如果路由規則定義多了之後,仍然很容易混淆,所以需要尋找更好的解決辦法。
2、利用完全匹配功能
現在我們來利用路由的完全匹配定義功能,把路由定義改成:
  1. 'URL_ROUTE_RULES' => array( //定義路由規則
  2.     'new/:id\d$'                    => 'News/read',
  3.     'new/:name$'    => 'News/read',
  4.     'new/:year\d/:month\d$'  => 'News/archive',
  5.  ),
複製代碼
在規則最後加上$符號之後,表示完整匹配當前的路由規則,就可以避免規則定義的衝突了。對於規則路由來說,簡單的理解就是URL裏面的參數數量或者類型約束要完全一致。
所以,如果我們訪問
  1. http://serverName/index.php/new/2012/03/01
複製代碼
的話,是不會匹配成功任何一條路由的。
3、利用正則路由
當然,解決問題的辦法總是不止一種,對於複雜的情況,我們不要忘了使用正則路由規則定義,在你找不到解決方案的時候,正則路由總能幫到你。
要實現上面的同樣路由功能的話,還可以用下面的規則定義:
  1. 'URL_ROUTE_RULES' => array( //定義路由規則
  2.     '/^new\/(\d+)$/'        => 'News/read?id=:1',
  3.     '/^new\/(\w+)$/'        => 'News/read?name=:1',
  4.     '/^new\/(\d{4})\/(\d{2})$/' => 'News/achive?year=:1&month=:2',
  5.  ),
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章