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