這篇文章主要介紹了thinkphp5 URL和路由的功能詳解與執行個體,現在分享給大家,也給大家做個參考。一起過來看看吧
前面的話
本文將詳細介紹thinkphp5URL和路由
URL訪問
ThinkPHP採用單一入口模式訪問應用,對應用的所有請求都定向到應用的入口檔案,系統會從URL參數中解析當前請求的模組、控制器和操作,下面是一個標準的URL訪問格式:
http://domainName/index.php/模組/控制器/操作
其中index.php就稱之為應用的入口檔案(注意入口檔案可以被隱藏,後面會提到)
模組在ThinkPHP中的概念其實就是應用目錄下面的子目錄,而官方的規範是目錄名小寫,因此模組全部採用小寫命名,無論URL是否開啟大小寫轉換,模組名都會強制小寫
應用的index模組的Index控制器定義如下:
<?phpnamespace app\index\controller;class Index{ public function index() { return 'index'; } public function hello($name = 'World') { return 'Hello,' . $name . '!'; }}
如果直接存取入口檔案的話,由於URL中沒有模組、控制器和操作,因此系統會訪問預設模組(index)下面的預設控制器(Index)的預設操作(index),因此下面的訪問是等效的:
http://tp5.com/index.php
http://tp5.com/index.php/index/index/index
如果要存取控制器的hello方法,則需要使用完整的URL地址
http://tp5.com/index.php/index/index/hello/name/thinkphp
訪問URL地址後頁面輸出結果為:
Hello,thinkphp!
由於name參數為選擇性參數,因此也可以使用
http://tp5.com/index.php/index/index/hello
訪問URL地址後頁面輸出結果為:
Hello,World!
預設情況下,URL地址中的控制器和操作名是不區分大小寫,因此下面的訪問其實是等效的:
http://tp5.com/index.php/index/Index/Index
http://tp5.com/index.php/index/INDEX/INDEX
如果控制器是駝峰的,例如定義一個HelloWorld控制器(application/index/controller/HelloWorld.php):
<?phpnamespace app\index\controller;class HelloWorld{ public function index($name = 'World') { return 'Hello,' . $name . '!'; }}
正確的URL訪問地址(該地址可以使用url方法產生)應該是
http://tp5.com/index.php/index/hello_world/index
系統會自動定位到HelloWorld控制器類去操作
如果使用
http://tp5.com/index.php/index/HelloWorld/index
將會報錯,並提示Helloworld控制器類不存在
如果希望嚴格區分大小寫訪問(這樣就可以支援駝峰法進行控制器訪問),可以在應用設定檔中設定:
// 關閉URL自動轉換(支援駝峰存取控制器)'url_convert' => false,
關閉URL自動轉換之後,必須使用下面的URL地址訪問(控制器名稱必須嚴格使用控制器類的名稱,不包含控制器尾碼):
http://tp5.com/index.php/index/Index/index
http://tp5.com/index.php/index/HelloWorld/index
如果伺服器環境不支援pathinfo方式的URL訪問,可以使用相容方式,例如:
http://tp5.com/index.php?s=/index/Index/index
其中變數s的名稱的可以配置的
5.0不再支援普通的URL訪問方式,所以下面的訪問是無效的,你會發現無論輸入什麼,訪問的都是預設的控制器和操作
http://tp5.com/index.php?m=index&c=Index&a=hello
參數傳入
通過操作方法的參數綁定功能,可以實現自動擷取URL的參數,仍然以上面的控制器為例,控制器代碼如下:
<?phpnamespace app\index\controller;class Index{ public function index() { return 'index'; } public function hello($name = 'World') { return 'Hello,' . $name . '!'; }}
當我們訪問
http://tp5.com/index.php/index/index/hello
就是訪問app\index\controller\Index控制器類的hello方法,因為沒有傳入任何參數,name參數就使用預設值World。如果傳入name參數,則使用:
http://tp5.com/index.php/index/index/hello/name/thinkphp
頁面輸出結果為:
Hello,thinkphp!
現在給hello方法增加第二個參數:
public function hello($name = 'World', $city = '') { return 'Hello,' . $name . '! You come from ' . $city . '.'; }
訪問地址為http://tp5.com/index.php/index/index/hello/name/thinkphp/city/shanghai
頁面輸出結果為:
Hello,thinkphp! You come from shanghai.
可以看到,hello方法會自動擷取URL地址中的同名參數值作為方法的參數值,而且這個參數的傳入順序不受URL參數順序的影響,例如下面的URL地址輸出的結果和上面是一樣的:
http://tp5.com/index.php/index/index/hello/city/shanghai/name/thinkphp
或者使用http://tp5.com/index.php/index/index/hello?city=shanghai&name=thinkphp
還可以進一步對URL地址做簡化,前提就是我們必須明確參數的順序代表的變數,我們更改下URL參數的擷取方式,把應用設定檔中的url_param_type參數的值修改如下:
// 按照參數順序擷取'url_param_type' => 1,
現在,URL的參數傳值方式就變成了嚴格按照操作方法的變數定義順序來傳值了,也就是說我們必須使用下面的URL地址訪問才能正確傳入name和city參數到hello方法:http://tp5.com/index.php/index/index/hello/thinkphp/shanghai
頁面輸出結果為:
Hello,thinkphp! You come from shanghai.
如果改變參數順序為http://tp5.com/index.php/index/index/hello/shanghai/thinkphp
頁面輸出結果為:
Hello,shanghai! You come from thinkphp.
顯然不是我們預期的結果。
同樣,我們試圖通過http://tp5.com/index.php/index/index/hello/name/thinkphp/city/shanghai
訪問也不會得到正確的結果
[注意]按順序綁定參數的話,操作方法的參數只能使用URL pathinfo變數,而不能使用get或者post變數
隱藏入口
可以去掉URL地址裡面的入口檔案index.php,但是需要額外配置WEB伺服器的重寫規則。
以Apache為例,需要在入口檔案的同級添加.htaccess檔案(官方預設內建了該檔案),內容如下
<IfModule mod_rewrite.c>Options +FollowSymlinks -MultiviewsRewriteEngine onRewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]</IfModule>
如果用的phpstudy,規則如下:
<IfModule mod_rewrite.c> Options +FollowSymlinks -Multiviews RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php [L,E=PATH_INFO:$1] </IfModule>
接下來就可以使用下面的URL地址訪問了
http://tp5.com/index/index/index
http://tp5.com/index/index/hello
如果使用的apache版本使用上面的方式無法正常隱藏index.php,可以嘗試使用下面的方式配置.htaccess檔案:
<IfModule mod_rewrite.c>Options +FollowSymlinks -MultiviewsRewriteEngine onRewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L]</IfModule>
如果是Nginx環境的話,可以在Nginx.conf中添加:
location / { // …..省略部分代碼 if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=/$1 last; break; }}
定義路由
URL地址裡面的index模組怎麼才能省略呢,預設的URL地址顯得有點長,下面就來說說如何通過路由簡化URL訪問。
我們在路由定義檔案(application/route.php)裡面添加一些路由規則,如下:
return [ // 添加路由規則 路由到 index控制器的hello操作方法 'hello/:name' => 'index/index/hello',];
該路由規則表示所有hello開頭的並且帶參數的訪問都會路由到index控制器的hello操作方法。
路由之前的URL訪問地址為:http://tp5.com/index/index/hello/name/thinkphp
定義路由後就只能訪問下面的URL地址http://tp5.com/hello/thinkphp
[注意]定義路由規則後,原來的URL地址將會失效,變成非法請求。
但這裡有一個小問題,如果我們只是訪問http://tp5.com/hello
將發生錯誤
事實上這是由於路由沒有正確匹配到,我們修改路由規則如下:
return [ // 路由參數name為可選 'hello/[:name]' => 'index/hello',];
使用[]把路由規則中的變數包起來,就表示該變數為可選,接下來就可以正常訪問了http://tp5.com/hello
當name參數沒有傳入值的時候,hello方法的name參數有預設值World,所以輸出的內容為 Hello,World!
除了路由設定檔中定義之外,還可以採用動態定義路由規則的方式定義,例如在路由設定檔(application/route.php)的開頭直接添加下面的方法:
use think\Route;Route::rule('hello/:name', 'index/hello');
完成的效果和使用配置方式定義是一樣的。
無論是配置方式還是通過Route類的方法定義路由,都統一放到路由設定檔application/route.php檔案中
[注意]路由配置不支援在模組設定檔中設定
【完整匹配】
前面定義的路由是只要以hello開頭就能進行匹配,如果需要完整匹配,可以使用下面的定義:
return [ // 路由參數name為可選 'hello/[:name]$' => 'index/hello',];
當路由規則以$結尾的時候就表示當前路由規則需要完整匹配。
當我們訪問下面的URL地址的時候:
http://tp5.com/hello // 正確匹配
http://tp5.com/hello/thinkphp // 正確匹配
http://tp5.com/hello/thinkphp/val/value // 不會匹配
【閉包定義】
還支援通過定義閉包為某些特殊的情境定義路由規則,例如:
return [ // 定義閉包 'hello/[:name]' => function ($name) { return 'Hello,' . $name . '!'; },];
或者
use think\Route;Route::rule('hello/:name', function ($name) { return 'Hello,' . $name . '!';});
[注意]閉包函數的參數就是路由規則中定義的變數
因此,當訪問下面的URL地址:http://tp5.com/hello/thinkphp
會輸出
Hello,thinkphp!
【設定URL分隔字元】
如果需要改變URL地址中的pathinfo參數分隔字元,只需要在應用設定檔(application/config.php)中設定:
// 設定pathinfo分隔字元'pathinfo_depr' => '-',
路由規則定義無需做任何改變,我們就可以訪問下面的地址:http://tp5.com/hello-thinkphp
【路由參數】
還可以約束路由規則的請求類型或者URL尾碼之類的條件,例如:
return [ // 定義路由的請求類型和尾碼 'hello/[:name]' => ['index/hello', ['method' => 'get', 'ext' => 'html']],];
上面定義的路由規則限制了必須是get請求,而且尾碼必須是html的,所以下面的訪問地址:
http://tp5.com/hello // 無效
http://tp5.com/hello.html // 有效
http://tp5.com/hello/thinkphp // 無效
http://tp5.com/hello/thinkphp.html // 有效
【變數規則】
接下來,嘗試一些複雜的路由規則定義滿足不同的路由變數。在此之前,首先增加一個控制器類如下:
<?phpnamespace app\index\controller;class Blog{ public function get($id) { return '查看id=' . $id . '的內容'; } public function read($name) { return '查看name=' . $name . '的內容'; } public function archive($year, $month) { return '查看' . $year . '/' . $month . '的歸檔內容'; }}
添加如下路由規則:
return [ 'blog/:year/:month' => ['blog/archive', ['method' => 'get'], ['year' => '\d{4}', 'month' => '\d{2}']], 'blog/:id' => ['blog/get', ['method' => 'get'], ['id' => '\d+']], 'blog/:name' => ['blog/read', ['method' => 'get'], ['name' => '\w+']],];
在上面的路由規則中,我們對變數進行的規則約束,變數規則使用Regex進行定義。
我們看下幾種URL訪問的情況
// 訪問id為5的內容
http://tp5.com/blog/5
// 訪問name為thinkphp的內容
http://tp5.com/blog/thinkphp
// 訪問2015年5月的歸檔內容
http://tp5.com/blog/2015/05
【路由分組】
上面的三個路由規則由雩都是blog打頭,所以我們可以做如下的簡化:
return [ '[blog]' => [ ':year/:month' => ['blog/archive', ['method' => 'get'], ['year' => '\d{4}', 'month' => '\d{2}']], ':id' => ['blog/get', ['method' => 'get'], ['id' => '\d+']], ':name' => ['blog/read', ['method' => 'get'], ['name' => '\w+']], ],];
對於這種定義方式,我們稱之為路由分組,路由分組一定程度上可以提高路由檢測的效率
【複雜路由】
有時候,還需要對URL做一些特殊的定製,例如如果要同時支援下面的訪問地址
http://tp5.com/blog/thinkphp
http://tp5.com/blog-2015-05
我們只要稍微改變路由定義規則即可:
return [ 'blog/:id' => ['blog/get', ['method' => 'get'], ['id' => '\d+']], 'blog/:name' => ['blog/read', ['method' => 'get'], ['name' => '\w+']], 'blog-<year>-<month>' => ['blog/archive', ['method' => 'get'], ['year' => '\d{4}', 'month' => '\d{2}']],];
對 blog-<year>-<month> 這樣的非正常規範,我們需要使用<變數名>這樣的變數定義方式,而不是 :變數名方式。
簡單起見,我們還可以把變數規則統一定義,例如:
return [ // 全域變數規則定義 '__pattern__' => [ 'name' => '\w+', 'id' => '\d+', 'year' => '\d{4}', 'month' => '\d{2}', ], // 路由規則定義 'blog/:id' => 'blog/get', 'blog/:name' => 'blog/read', 'blog-<year>-<month>' => 'blog/archive',];
在__pattern__中定義的變數規則我們稱之為全域變數規則,在路由規則裡面定義的變數規則我們稱之為局部變數規則,如果一個變數同時定義了全域規則和局部規則的話,當前的局部規則會覆蓋全域規則的,例如:
return [ // 全域變數規則 '__pattern__' => [ 'name' => '\w+', 'id' => '\d+', 'year' => '\d{4}', 'month' => '\d{2}', ], 'blog/:id' => 'blog/get', // 定義了局部變數規則 'blog/:name' => ['blog/read', ['method' => 'get'], ['name' => '\w{5,}']], 'blog-<year>-<month>' => 'blog/archive',];
URL產生
定義路由規則之後,可以通過Url類來方便的產生實際的URL地址(路由地址),針對上面的路由規則,我們可以用下面的方式產生URL地址。
// 輸出 blog/thinkphpUrl::build('blog/read', 'name=thinkphp');Url::build('blog/read', ['name' => 'thinkphp']);// 輸出 blog/5Url::build('blog/get', 'id=5');Url::build('blog/get', ['id' => 5]);// 輸出 blog/2015/05Url::build('blog/archive', 'year=2015&month=05');Url::build('blog/archive', ['year' => '2015', 'month' => '05']);
[注意]build方法的第一個參數使用路由定義中的完整路由地址
還可以使用系統提供的助手函數url來簡化
url('blog/read', 'name=thinkphp');// 等效於Url::build('blog/read', 'name=thinkphp');
通常在模板檔案中輸出的話,可以使用助手函數,例如:
{:url('blog/read', 'name=thinkphp')}
如果我們的路由規則發生調整,產生的URL地址會自動變化
如果你配置了url_html_suffix參數的話,產生的URL地址會帶上尾碼,例如:
'url_html_suffix' => 'html',
那麼產生的URL地址 類似
blog/thinkphp.html blog/2015/05.html
如果你的URL地址全部採用路由方式定義,也可以直接使用路由規則來定義URL產生,例如:
url('/blog/thinkphp');Url::build('/blog/8');Url::build('/blog/archive/2015/05');
產生方法的第一個參數一定要和路由定義的路由地址保持一致,如果你的路由地址比較特殊,例如使用閉包定義的話,則需要手動給路由指定標識,例如:
// 添加hello路由辨別碼Route::rule(['hello','hello/:name'], function($name){ return 'Hello,'.$name;});// 根據路由辨別碼快速產生URLUrl::build('hello', 'name=thinkphp');// 或者使用Url::build('hello', ['name' => 'thinkphp']);