## 訪問控制器
ThinkPHP引入了分層控制器的概念,通過URL訪問的控制器為訪問控制器層(Controller)或者主控制器,訪問控制器是由`\think\App`類負責調用和實例化的,無需手動實例化。
URL解析和路由后,會把當前的URL地址解析到 **[ 模塊/控制器/操作 ]**,其實也就是執行某個控制器類的某個操作方法,下面是一個示例:
~~~
namespace app\index\controller;
class New
{
public function index(){
return 'index';
}
public function add(){
return 'add';
}
public function edit($id){
return 'edit:'.$id;
}
}
~~~
當前定義的主控制器位于index模塊下面,所以當訪問不同的URL地址的頁面輸出如下:
~~~
http://serverName/index/new/index // 輸出 index
http://serverName/index/new/add // 輸出 add
http://serverName/index/new/edit/id/5 // 輸出 edit:5
~~~
> 新版的控制器可以不需要繼承任何基類,當然,你可以定義一個公共的控制器基礎類來被繼承,也可以通過控制器擴展來完成不同的功能(例如Restful實現)。
如果不經過路由訪問的話,URL中的控制器名會首先強制轉為小寫,然后再解析為駝峰法實例化該控制器。
## 渲染模板和輸出
默認的情況下,如果不需要渲染模板,無需繼承`\think\Controller`類,如果需要進行模板渲染等操作,可以改為:
~~~
namespace app\index\controller;
use think\Controller;
class New extends Controller
{
public function index(){
return $this->fetch();
}
public function add(){
return $this->fetch();
}
public function edit($id){
return $this->show('edit:'.$id);
}
}
~~~
> 新版控制器一般不需要任何輸出,直接return即可。
## 多層控制器
新版支持任意層次的控制器,并且支持路由,例如:
~~~
namespace app\index\controller\one;
use think\Controller;
class New extends Controller
{
public function index(){
return $this->fetch();
}
public function add(){
return $this->fetch();
}
public function edit($id){
return $this->show('edit:'.$id);
}
}
~~~
訪問地址可以使用
~~~
http://serverName/index.php/index/one.new/index
~~~
## 空操作和空控制器
### 空操作
空操作是指系統在找不到指定的操作方法的時候,會定位到空操作(`_empty`)方法來執行,利用這個機制,我們可以實現錯誤頁面和一些URL的優化。
例如,下面我們用空操作功能來實現一個城市切換的功能。
我們只需要給City類定義一個`_empty` (空操作)方法:
~~~
<?php
namespace app\index\controller;
class City {
public function _empty($name){
//把所有城市的操作解析到city方法
return $this->showCity($name);
}
//注意 showCity方法 本身是 protected 方法
protected function showCity($name){
//和$name這個城市相關的處理
return '當前城市' . $name;
}
}
~~~
接下來,我們就可以在瀏覽器里面輸入
~~~
http://serverName/index/city/beijing/
http://serverName/index/city/shanghai/
http://serverName/index/city/shenzhen/
~~~
由于City并沒有定義beijing、shanghai或者shenzhen操作方法,因此系統會定位到空操作方法 _empty中去解析,_empty方法的參數就是當前URL里面的操作名,因此會看到依次輸出的結果是:
~~~
當前城市:beijing
當前城市:shanghai
當前城市:shenzhen
~~~
### 空控制器
空控制器的概念是指當系統找不到指定的控制器名稱的時候,系統會嘗試定位空控制器(Error),利用這個機制我們可以用來定制錯誤頁面和進行URL的優化。
現在我們把前面的需求進一步,把URL由原來的
~~~
http://serverName/index/city/shanghai/
~~~
變成
~~~
http://serverName/index/shanghai/
~~~
這樣更加簡單的方式,如果按照傳統的模式,我們必須給每個城市定義一個控制器類,然后在每個控制器類的index方法里面進行處理。 可是如果使用空模塊功能,這個問題就可以迎刃而解了。 我們可以給項目定義一個Error控制器類
~~~
<?php
namespace app\index\controller;
class Error {
public function index(){
//根據當前模塊名來判斷要執行那個城市的操作
$cityName = CONTROLLER_NAME;
return $this->city($cityName);
}
//注意 city方法 本身是 protected 方法
protected function city($name){
//和$name這個城市相關的處理
return '當前城市' . $name;
}
}
~~~
接下來,我們就可以在瀏覽器里面輸入
~~~
http://serverName/index/beijing/
http://serverName/index/shanghai/
http://serverName/index/shenzhen/
~~~
由于系統并不存在beijing、shanghai或者shenzhen控制器,因此會定位到空控制器(Error)去執行,會看到依次輸出的結果是:
~~~
當前城市:beijing
當前城市:shanghai
當前城市:shenzhen
~~~
空控制器和空操作還可以同時使用,用以完成更加復雜的操作。
## Rest控制器
如果需要讓你的控制器支持RESTful的話,可以使用Rest控制器,在定義訪問控制器的時候直接繼承Think\Controller\Rest即可,例如:
~~~
namespace app\index\controller;
use think\controller\Rest;
class New extends Rest
{
}
~~~
### RESTFul方法定義
RESTFul方法和標準模式的操作方法定義主要區別在于,需要對請求類型和資源類型進行判斷,大多數情況下,通過路由定義可以把操作方法綁定到某個請求類型和資源類型。如果你沒有定義路由的話,需要自己在操作方法里面添加判斷代碼,示例:
~~~
namespace app\index\controller;
use think\controller\Rest;
class New extends Rest{
Public function rest() {
switch ($this->_method){
case 'get': // get請求處理代碼
if ($this->_type == 'html'){
}elseif($this->_type == 'xml'){
}
break;
case 'put': // put請求處理代碼
break;
case 'post': // put請求處理代碼
break;
}
}
}
~~~
在Rest操作方法中,可以使用$this->_type獲取當前訪問的資源類型,用$this->_method獲取當前的請求類型。
Rest類還提供了response方法用于REST輸出:
response輸出數據
|||
|---|---|
| 用法 | response($data,$type='',$code=200) |
| 參數 | data(必須):要輸出的數據<br/>type(可選):要輸出的類型<br/>支持restOutputType參數允許的類型,如果為空則取restDefaultType參數設置值<br/>code (可選):HTTP狀態 |
| 返回值 | 無 |
Response方法會自動對data數據進行輸出類型編碼,目前支持的包括xml json html。
除了普通方式定義Restful操作方法外,系統還支持另外一種自動調用方式,就是根據當前請求類型和資源類型自動調用相關操作方法。系統的自動調用規則是:
| 定義規范 | 說明 |
|---|---|
| 操作名_提交類型_資源后綴 | 標準的Restful方法定義,例如 read_get_pdf |
| 操作名_資源后綴 | 當前提交類型和restDefaultMethod相同的時候,例如read_pdf |
| 操作名_提交類型 | 當前資源后綴和restDefaultType相同的時候,例如read_post |
這種方式的rest方法定義采用了空操作機制,所以要使用這種方式的前提就是不能為當前操作定義方法,如果檢測到相關的restful方法則不再檢查后面的方法規范,例如我們定義了InfoController如下:
~~~
namespace app\index\controller;
use think\controller\Rest;
class Info extends Rest{
Public function read_get_xml($id){
// 輸出id為1的Info的XML數據
}
Public function read_xml($id){
// 輸出id為1的Info的XML數據
}
Public function read_json($id){
// 輸出id為1的Info的json數據
}
}
~~~
如果我們訪問的URL是:
~~~
http://serverName/index/info/read/id/1.xml
~~~
假設我們沒有定義路由,這樣訪問的是Info模塊的read操作,那么上面的請求會調用Info類的 read_get_xml方法,而不是read_xml方法,但是如果訪問的URL是:
~~~
http://serverName/index/info/read/id/1.json
~~~
那么則會調用read_json方法。
## 分層控制器
除了訪問控制器外,我們還可以定義其他分層控制器類,這些分層控制器是不能夠被URL訪問直接調用到的,只能在訪問控制器、模型類的內部,或者視圖模板文件中進行調用。
例如,我們定義New事件控制器如下:
~~~
namespace app\index\event;
class New {
public function insert(){
echo 'insert';
}
public function update($id){
echo 'update:'.$id;
}
public function delete($id){
echo 'delete:'.$id;
}
}
~~~
定義完成后,就可以用下面的方式實例化并調用方法了:
~~~
$Event = \think\Loader::controller('New','event');
$Event->update(5); // 輸出 update:5
$Event->delete(5); // 輸出 delete:5
~~~
為了方便調用,系統提供了A快捷方法直接實例化多層控制器,例如:
~~~
$Event = A('New','event');
$Event->update(5); // 輸出 update:5
$Event->delete(5); // 輸出 delete:5
~~~
支持跨模塊調用,例如:
~~~
$Event = A('Admin/New','event');
$Event->update(5); // 輸出 update:5
~~~
表示實例化Admin模塊的New控制器類,并執行update方法。
除了實例化分層控制器外,還可以直接調用分層控制器類的某個方法,例如:
~~~
$result = \think\Loader::action('New/update',['id'=>5],'event'); // 輸出 update:5
~~~
也可以使用快捷R方法實現相同的功能:
~~~
$result = R('New/update',['id'=>5],'event'); // 輸出 update:5
~~~
利用分層控制器的機制,我們可以用來實現Widget(其實就是在模板中調用分層控制器),例如:
定義`index\widget\New`控制器類如下:
~~~
namespace \app\index\widget;
class New {
public function header(){
echo 'header';
}
public function left(){
echo 'left';
}
public function menu($name){
echo 'menu:'.$name;
}
}
~~~
我們在模板文件中就可以直接調用`app\index\widget\New`分層控制器了,例如:
~~~
<?php R('New/header','','widget');?>
<?php R('New/menu',['name'=>'think'],'widget');?>
~~~
框架還提供了W方法用于簡化Widget控制器的調用,例如可以直接使用:
~~~
<?php W('New/header');?>
<?php W('New/menu',['name'=>'think']);?>
~~~