ThinkPHP的控制器層由核心控制器和業務控制器組成,核心控制器由系統內部的App類完成,負責應用(包括模塊和操作)的調度控制,包括HTTP請求攔截和轉發、加載配置等,業務控制器則由用戶定義的Action類或者其他控制器類完成。
我們通過前面的學習,已經了解了基本的控制器用法,這一篇我們來講述下控制器的一些特性和高級用法,來探索ThinkPHP控制器的神秘外衣。[-more-]
Action參數綁定
在前面的內容中,我們涉及的所有操作方法都是沒有任何參數的,其實從3.1版本開始,可以支持參數綁定功能。Action參數綁定的原理是把URL中的參數(不包括分組、模塊和操作名)和控制器的操作方法中的參數(按變量名)進行綁定。
例如,我們給Blog模塊定義了兩個操作方法read和archive方法,并且給read操作需要指定一個id參數,archive方法指定年份(year)和月份(month)兩個參數。為了演示方便,我們省去了具體操作方法的業務代碼,僅僅用echo 輸出當前的參數。
class BlogAction extends Action{
public function read($id){
echo 'id='.$id;
}
public function archive($year='2012',$month='01'){
echo 'year='.$year.'&month='.$month;
}
}
URL的訪問地址分別是:
http://serverName/index.php/Blog/read/id/5
http://serverName/index.php/Blog/archive/year/2012/month/03
兩個URL地址中的id參數和year和month參數會自動和read操作方法以及archive操作方法的同名參數綁定。
輸出的結果依次是:
id=5
year=2012&month=03
Action參數綁定的參數必須和URL中傳入的參數名稱一致,但是參數順序不需要一致。也就是說
http://serverName/index.php/Blog/archive/month/03/year/2012
和上面的訪問結果是一致的,URL中的參數順序和操作方法中的參數順序都可以隨意調整,關鍵是確保參數名稱一致即可。
如果用戶訪問的URL地址是(至于為什么會這么訪問暫且不提):
http://serverName/index.php/Blog/read/
那么會拋出下面的異常提示:
參數錯誤:id
報錯的原因很簡單,因為在執行read操作方法的時候,id參數是必須傳入參數的,但是方法無法從URL地址中獲取正確的id參數信息。由于我們不能相信用戶的任何輸入,因此建議你給read方法的id參數添加默認值,例如:
public function read($id=0){
echo 'id='.$id;
}
這樣,當我們訪問
http://serverName/index.php/Blog/read/
的時候 就會輸出
id=0
當我們訪問
http://serverName/index.php/Blog/archive/
的時候,輸出:
year=2012&month=01
參數綁定功能不受路由影響,從路由中匹配和URL傳入的參數一樣有效,并且綁定的參數如果需要特殊處理和過濾的話,需要另行處理。
空模塊和空操作
空操作是指系統在找不到指定的操作方法的時候,會定位到空操作(_empty)方法來執行,利用這個機制,我們可以實現錯誤頁面和一些URL的優化。
例如,下面我們用空操作功能來實現一個城市切換的功能。
我們只需要給CityAction類定義一個_empty (空操作)方法:
<?php
class CityAction extends Action{
public function _empty($name){
//把所有城市的操作解析到city方法
$this->city($name);
}
//注意 city方法 是 protected 方法
protected function city($name){
//和$name這個城市相關的處理
echo '當前城市' . $name;
}
}
接下來,我們就可以在瀏覽器里面輸入
http://serverName/index.php/City/beijing/
http://serverName/index.php/City/shanghai/
http://serverName/index.php/City/shenzhen/
由于CityAction并沒有定義beijing、shanghai或者shenzhen操作方法,因此系統會定位到空操作方法
_empty中去解析,_empty方法的參數就是當前URL里面的操作名,因此會看到依次輸出的結果是:
當前城市:beijing
當前城市:shanghai
當前城市:shenzhen
空模塊的概念是指當系統找不到指定的模塊名稱的時候,系統會嘗試定位空模塊(EmptyAction),利用這個機制我們可以用來定制錯誤頁面和進行URL的優化。現在我們把前面的需求進一步,把URL由原來的
http://serverName/index.php/City/shanghai/
變成
http://serverName/index.php/shanghai/
這樣更加簡單的方式,如果按照傳統的模式,我們必須給每個城市定義一個Action類,然后在每個Action類的index方法里面進行處理。 可是如果使用空模塊功能,這個問題就可以迎刃而解了。 我們可以給項目定義一個EmptyAction類
<?php
class EmptyAction extends Action{
public function index(){
//根據當前模塊名來判斷要執行那個城市的操作
$cityName = MODULE_NAME;
$this->city($cityName);
}
//注意 city方法 本身是 protected 方法
protected function city($name){
//和$name這個城市相關的處理
echo '當前城市' . $name;
}
}
接下來,我們就可以在瀏覽器里面輸入
http://serverName/index.php/beijing/
http://serverName/index.php/shanghai/
http://serverName/index.php/shenzhen/
由于系統并不存在beijing、shanghai或者shenzhen模塊,因此會定位到空模塊(EmptyAction)的默認操作(index)去執行,會看到依次輸出的結果是:
當前城市:beijing
當前城市:shanghai
當前城市:shenzhen
空模塊和空操作還可以同時使用,用以完成更加復雜的操作。
前置和后置操作
如果當前訪問的操作是存在的,系統會檢測當前操作是否具有前置和后置操作,如果存在就會按照順序執行,前置和后置操作的方法名是在要執行的方法前面加 _before_和_after_,例如:
class IndexAction extends Action{
//前置操作方法
public function _before_index(){
echo 'before<br/>';
}
public function index(){
echo 'index<br/>';
}
//后置操作方法
public function _after_index(){
echo 'after<br/>';
}
}
如果我們訪問
http://serverName/index.php
結果會輸出
before
index
after
對于任何操作方法我們都可以按照這樣的規則來定義前置和后置方法。
需要注意的是,如果在操作方法里面使用了exit或者error方法的話 有可能不會再執行后置方法了。
跳轉和重定向
系統的Action類內置了兩個頁面跳轉方法error和success,分別用于錯誤(提示)跳轉和成功(提示)跳轉。兩個方法都會輸出一個提示信息頁面,然后自動跳轉到指定的地址。如果當前請求是ajax方式的話,則會自動進行ajax數據返回。下面是一個簡單的例子:
$User = M('User'); //實例化User對象
$result = $User->add($data);
if($result){
//設置成功后跳轉頁面的地址,默認的返回頁面是$_SERVER['HTTP_REFERER']
$this->success('新增成功', '/User/list');
} else {
//錯誤頁面的默認跳轉頁面是返回前一頁,通常不需要設置
$this->error('新增失敗');
}
Success和error方法都有對應的模板,并且是可以設置的,默認的設置是系統模板:
//默認錯誤跳轉對應的模板文件
'TMPL_ACTION_ERROR' => THINK_PATH . 'Tpl/dispatch_jump.tpl',
//默認成功跳轉對應的模板文件
'TMPL_ACTION_SUCCESS' => THINK_PATH . 'Tpl/dispatch_jump.tpl',
我們可以在項目配置文件中修改為使用項目內部的模板文件
//默認錯誤跳轉對應的模板文件
'TMPL_ACTION_ERROR' => 'Public:error',
//默認成功跳轉對應的模板文件
'TMPL_ACTION_SUCCESS' => 'Public:success',
如果你的操作不需要任何提示頁面,也可以直接使用頁面重定向功能。
系統提供了redirect方法實現頁面的重定向功能。
例如:
//重定向到New模塊的Category操作
$this->redirect('New/category', array('cate_id' => 2), 5, '頁面跳轉中...');
上面的用法是停留5秒后跳轉到New模塊的category操作,并且顯示頁面跳轉中字樣,重定向后會改變當前的URL地址。
redirect方法的第一個參數和第二個參數的配合來完成實際的URL地址的組裝,用法和U函數的用法基本一致。
如果你僅僅是想重定向要一個指定的URL地址,而不是到某個模塊的操作方法,可以直接使用redirect函數重定向,例如:
//重定向到指定的URL地址
redirect('/New/category/cate_id/2', 5, '頁面跳轉中...');
Redirect方法的第一個參數是要跳轉的實際URL地址。
AJAX返回
目前的很多WEB應用中大量運用了ajax操作,系統也提供了一個用于ajax數據返回的方法ajaxReturn方法,用法:
$this->ajaxReturn(返回數據[,返回數據格式]);
目前已經支持的ajax返回數據格式包括:XML JSON JSONP EVAL。
下面是一個簡單的例子:
$data['status'] = 1;
$data['info'] = 'info';
$data['data'] = $data;
$data['url'] = $url;
$this->ajaxReturn($data);
在客戶端就可以接收傳遞的$data數據,可以通過ajaxReturn方法傳遞任意數據到客戶端。如果不指定返回格式的話,默認為JSON格式返回,也可以指定數據格式返回:
$this->ajaxReturn($data,'XML');
頁面跳轉方法success和error如果在ajax請求方式下面會自動調用ajaxReturn方法,例如:
$this->success('發布成功',$url);
等效于使用:
$data['info'] = '發布成功';
$data['url'] = $url;
$data['status'] = 1;
$this->ajaxReturn($data);
在客戶端就可以接收返回的包含info、url和status值的data數據。
你無需擔心客戶端怎么發送ajax請求給ThinkPHP,ThinkPHP可以自動識別大部分類庫的ajax請求,包括JqueryAjax,但某些Flash上傳組件可能無法準確識別,請確保在請求的URL地址中傳入ajax=1參數,這樣就能讓ThinkPHP識別為Ajax操作。
頁面請求類型
如果需要根據當前的頁面請求類型來做出不同的處理,可以使用系統提供的幾個常量:
REQUEST_METHOD | 當前請求類型 |
IS_GET | 是否GET請求 |
IS_POST | 是否POST請求 |
IS_PUT | 是否PUT請求 |
IS_DELETE | 是否DELETE請求 |
IS_AJAX | 是否AJAX請求 |
舉例如下:
class UserAction extends Action{
public function update(){
if (IS_POST){
$User = M('User');
$User->create();
$User->save();
$this->success('保存完成');
}else{
$this->error('非法請求');
}
}
}
偽靜態
默認情況下,ThinkPHP可以支持所有的靜態后綴,并且會記錄當前的偽靜態后綴到常量__EXT__,但不會影響正常的頁面訪問。
例如:
http://serverName/User/3.html
http://serverName/User/3.shtml
http://serverName/User/3.xml
http://serverName/User/3.pdf
都可以正常訪問,如果要獲取當前訪問的偽靜態后綴,通過常量__EXT__獲取即可。
如果希望統一偽靜態后綴,可以設置:
'URL_HTML_SUFFIX'=>'html'
現在則只能訪問
http://serverName/User/3.html
也可以支持允許多個后綴,例如:
'URL_HTML_SUFFIX'=>'html|shtml|xml' // 多個用 | 分割
這樣,當訪問http://serverName/User/3.pdf的時候會報系統錯誤。
是實際應用中,我們可以根據當前的URL訪問后綴來做出不同的輸出處理。
多層控制器
3.1版本開始增加了多層業務控制器的支持,給中大型應用提供了方便,例如我們可以分為業務控制器和事件控制器:
Action/UserAction //用于用戶的業務邏輯控制和調度
Event/UserEvent //用于用戶的事件響應操作
UserAction負責外部交互響應,通過URL請求響應,例如 http://serverName/User/index,而UserEvent 負責內部的事件響應,并且只能在內部調用
A('User','Event');
所以是和外部隔離的。多層控制器的劃分也不是強制的,可以根據項目的需要自由分層。控制器分層里面可以根據需要調用分層模型,也可以調用不同的目錄的視圖模板。
總結
本篇涉及到的ThinkPHP的控制器特性包括空模塊和空操作、前置和后置操作、參數綁定、偽靜態、跳轉和重定向、ajax返回、請求類型,而新版的多層控制器的特性更是值得回味。