多層控制器的使用
TP3.23對控制器做了更加細(xì)致的分層,除了默認(rèn)的Controller
層吹泡,還可以自定義事件控制層Event
。
建立的方法是:
- 在模塊第一級目錄揭朝,即
Controller
層的同級目錄潭袱,新建文件夾Event
- 在
Event
文件夾中屯换,新建文件AdminEvent.class.php
- 在新建文件中輸入代碼
namespace Admin\Event;
// use Think\Controller;
// 如果不使用到controller的功能
// 可以不用添加該命名空間引用 也不用繼承controller
class AdminEvent extents Controller {
public function test() {
echo 'Hello World !';
}
}
系統(tǒng)默認(rèn)的控制器(即訪問控制器)是Controller
嘉抓,因此在瀏覽器輸入http://localhost/forum/index.php/Admin/admin/test
是無法進(jìn)行訪問的抑片,Event
控制器僅在內(nèi)部方法方法中進(jìn)行訪問。當(dāng)然可以通過DEFAULT_C_LAYER
來修改訪問控制器的名稱疾牲。但建議不這么做鸥跟。
定義了事件控制器,對系統(tǒng)模塊的構(gòu)建有很大的好處枫匾∏从牵可以將瀏覽器的請求與內(nèi)部事件處理相隔離沾谓,使整個業(yè)務(wù)邏輯看上去更為簡潔明了均驶。
如一個后臺系統(tǒng),有多個頁面腾它,都需要對用戶是否登錄進(jìn)行檢測瞒滴,如果這個檢測都在Controller
進(jìn)行的話妓忍,整個業(yè)務(wù)邏輯會顯得很混亂,而且可能會有代碼冗余碘举。
這時可以通過Event
事件控制器來定義一個檢測方法引颈,然后在Controller
層進(jìn)行調(diào)用,這樣代碼不僅更為簡練售滤,業(yè)務(wù)邏輯也更為清晰完箩。
Event控制器
namespace Admin\Event;
use Think\Controller;
class AdminEvent extents Controller {
/**
* 登錄檢測 檢測session中用戶
* @param 類型 參數(shù)名 含義
* @return 類型
*/
function checkLogin() {
$value = session('UserName');
if(!isset($value)) {
$this->error('請登錄后訪問' , U('admin/login'));
}
}
}
Controller控制器
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
/**
* 后臺首頁面
*/
public function index(){
A('Admin' , 'Event')->checkLogin();
$this->assign('title','后臺');
$this->display();
}
/**
- 文章顯示控制
- @param
- @return
*/
public function article() {
A('Admin' , 'Event')->checkLogin();
$this->assign('title','文章-后臺');
$this->display();
}
}
這樣無論是從那種鏈接登入后臺粱快,都會進(jìn)行用戶檢測事哭,不僅提高了安全性降盹,而且代碼邏輯清晰澎现。
我們可以將數(shù)據(jù)庫添加操作在Event
控制器中完成,在Controller
控制器只進(jìn)行模板輸出妹蔽,接受表單提交胳岂。
以上乳丰。
初始化操作
在Think\Controller.class.php
的構(gòu)造方法中進(jìn)行視圖類實(shí)例化后汞斧,會檢測是否存在一個_initialize
方法粘勒。如果存在庙睡,就會事先調(diào)用該方法。
這就是控制器的初始化操作雕擂。在控制器類中定義_initialize
方法暂刘,在操作其他方法之前都會先執(zhí)行該方法。
如:
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
# 初始化操作
public function _initialize() {
echo '頁面初始化捂刺,請等待<br/>';
}
# 主頁
public function index() {
echo 'index';
}
}
在瀏覽器輸入http://localhost/forum/index.php/Admin
,則會輸出
頁面初始化募寨,請等待
index
有了這個方法族展,我們上面所講的登錄檢測就能夠再次進(jìn)行簡化了。即在_initialize
方法中進(jìn)行是否登錄的檢測拔鹰。但在_initialize
添加了登錄檢測后仪缸,我們不能夠?qū)⑴c登錄有關(guān)的內(nèi)容放入AdminController
控制器中了,否則會不停的跳轉(zhuǎn)到error頁面恰画,而無法顯示登陸頁面。
我們可以將登錄
與注冊
同時拿出焕妙,新建一個控制器,名字叫UserController
果复。在這個頁面進(jìn)行登陸與注冊功能私植,而error頁面跳轉(zhuǎn)到該頁面。
UserController
namespace Admin\Controller;
use Think\Controller;
class UserController extends Controller {
/**
* 用戶登錄
*/
public function login() {
echo '用戶登錄';
}
/**
* 用戶注冊
*/
public function register() {
echo '用戶注冊';
}
}
AdminController
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
# 初始化操作
public function _initialize() {
A('Admin' , 'Event')->checkLogin();
}
/**
* 后臺首頁面
*/
public function index(){
$this->assign('title','后臺');
$this->display();
}
/**
- 文章顯示控制
- @param
- @return
*/
public function article() {
$this->assign('title','文章-后臺');
$this->display();
}
}
AdminEvent
namespace Admin\Event;
use Think\Controller;
class AdminEvent extents Controller {
/**
* 登錄檢測 檢測session中用戶
* @param 類型 參數(shù)名 含義
* @return 類型
*/
function checkLogin() {
$value = session('UserName');
if(!isset($value)) {
$this->error('請登錄后訪問' , U('user/login'));
}
}
}
這樣一個比較完善的后臺登陸就完成了(當(dāng)然還未實(shí)現(xiàn)具體的業(yè)務(wù)邏輯)逆趋,比較最原始的方法惠窄,代碼簡潔清晰了很多蒋腮,也完全符合MVC
設(shè)計思想。
前置與后置
與初始化設(shè)置類似浙踢,TP提供了前置與后置操作,要進(jìn)行前后置操作需要真實(shí)的方法蓝翰,系統(tǒng)在執(zhí)行該方法前會進(jìn)行檢測,如果定義了前后置操作蚓挤,則會安照順序進(jìn)行。定義的方法是在執(zhí)行方法前加入_before_
或_after_
,如
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
public function _before_index() {}
public function index() {}
public function _after_index() {}
}
在訪問http://localhost/forum/index.php/Admin
的時候化漆,就會先執(zhí)行_before_index
座云,在執(zhí)行index
,最后執(zhí)行_after_index
锣夹。注意的是如果在index
中定義了斷點(diǎn)方法链患,如die
婉宰,exit
歌豺,跳轉(zhuǎn)方法error
,success
就不會繼續(xù)執(zhí)行_after_index
心包。
這個前后置方法类咧,我暫時還未想到有什么比較有用的地方,先寫出來蟹腾,以后想到了痕惋,在進(jìn)行添加。
空操作與空控制器
按照ThinkPHP
的URL
命名規(guī)則(pathinfo模式)娃殖,入口文件之后的操作成為
模塊
控制器
操作
如http://servername/index.php/模塊/控制器/操作/[參數(shù)名/參數(shù)值…]
空操作
就是指系統(tǒng)找不到url指定的操作方法值戳,此時就會報錯,可以使用定義空操作方法來進(jìn)行避免炉爆。如:
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
/**
* 后臺首頁面
*/
public function index(){
$this->assign('title','后臺');
$this->display();
}
/**
- 空操作防止
- @param 無
- @return 無
*/
public function _empty() {
$this->display('404');
}
}
定義后堕虹,在輸入錯誤的操作方法時,就會顯示_empty
中的內(nèi)容芬首。你可以定義一個404頁面
赴捞,在_empty
中進(jìn)行跳轉(zhuǎn)。
空控制器
與空操作類似衩辟,訪問不存在的控制器就會產(chǎn)生空控制器操作,避免該錯誤的方法是波附,在Controller
中定義一個EmptyController
控制器艺晴,在該控制器中同樣定義_empty
方法昼钻。
namespace Admin\Controller;
use Think\Controller;
class EmptyController extends Controller {
/**
- 空操作防止
- @param 無
- @return 無
*/
public function _empty() {
$this->display('admin/404');
}
}
404頁面
對于404
頁面的定制,如果沒有特殊需求的同學(xué)封寞,建議使用騰訊的404公益頁面
然评,只要嵌入一小段JS
代碼,就會跳轉(zhuǎn)到騰訊的尋找失蹤兒童的404
頁面狈究。雖然可能和你的網(wǎng)站風(fēng)格不統(tǒng)一碗淌,但是每多一個看見該頁面的人,就可能加大找回一個失蹤兒童的幾率抖锥。一小段代碼亿眠,獻(xiàn)出一段愛心,你值得擁有磅废。
// 騰訊404頁面
<script type="text/javascript" src="http://www.qq.com/404/search_children.js" charset="utf-8" homePageUrl="http://yoursite.com/yourPage.html" homePageName="回到我的主頁"></script>
Action參數(shù)綁定
在說Action
參數(shù)綁定前纳像,先說說與之相關(guān)的問題。在學(xué)習(xí)Action參數(shù)綁定
之前拯勉,我獲取GET
參數(shù)的方式是怎么樣竟趾?
自然是利用$_GET
來獲取URL
上的參數(shù),如:
# 我要點(diǎn)擊一個文章鏈接宫峦,來進(jìn)行文章編輯 url如下
http://localhost/forum/index.php/Admin/admin/article/act/edit/aid/2
# 然后通過$_GET來獲取act aid得到參數(shù)岔帽,來判斷進(jìn)行的操作以及操作的文章id
if($_GET['act'] == 'edit') {
# 文章編輯 利用 aid從數(shù)據(jù)庫取出文章進(jìn)行編輯
}
這種方式雖然可行,卻不夠優(yōu)雅导绷,作為一個立志成為攻城獅的程序員來說犀勒,要想盡方法使你的代碼更加簡潔,更加優(yōu)雅诵次。
這時來看看Action參數(shù)綁定
账蓉,什么是Action參數(shù)綁定
?
Action參數(shù)綁定是通過直接綁定URL地址中的變量作為操作方法的參數(shù)逾一, 可以簡化方法的定義甚至路由的解析铸本。
說的還挺繞,其實(shí)就是把GET
形式傳遞的參數(shù)直接綁定到你的操作方法上遵堵,你就能夠直接訪問箱玷,而不用通過$_GET
。
舉個例子陌宿,還是上面的鏈接http://localhost/forum/index.php/Admin/admin/article/act/edit/aid/2
锡足。
定義操作方法如下:
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
/**
* 文章顯示控制器
* @param:$act string 進(jìn)行的文章操作 默認(rèn)值空 -> 顯示所有文章
* @param:$aid int 文章的主鍵id 默認(rèn)值0 -> 顯示所有文章
*/
public function article($act='' , $aid = 0) {
if(empty($act) || empty($aid) ) {
# 顯示所有文章
} else {
# 進(jìn)行想要的操作
echo '你要進(jìn)行的操作是'.$act.',你想要'.$act.'的文章id是'.$aid;
}
}
}
訪問上面的鏈接壳坪,結(jié)果為
你要進(jìn)行的操作是edit舶得,你想要edit的文章id是2
這就是Action參數(shù)綁定
。要使用這種方式需要開啟URL_PARAMS_BIND
設(shè)置(默認(rèn)設(shè)置true)爽蝴。
Action參數(shù)綁定
有兩種形式
按照變量名綁定
按照變量順序綁定
將URL_PARAMS_BIND_TYPE
的值設(shè)置成0沐批,按照變量名綁定译红,設(shè)置成1米丘,按照變量順序綁定月帝。
按照字面意思也可以理解阱扬,按照變量名綁定即尋找get參數(shù)時,按照操作方法中定義的變量名去尋找相應(yīng)的值躺彬。如果沒有就報錯煤墙。這也是最常用的方式。
按照變量順序綁定宪拥,即按照url上get參數(shù)的順序去給操作方法上的變量賦值仿野,這樣在url上的參數(shù)就能夠隨意變換位置,同時url上get參數(shù)也可以隱藏變量名江解。
舉例說明一下:
按照變量名綁定
鏈接 http://localhost/forum/index.php/Admin/admin/article/act/edit/aid/2
那么打印出來的結(jié)果
你要進(jìn)行的操作是edit设预,你想要edit的文章id是2
鏈接 http://localhost/forum/index.php/Admin/admin/article/aid/2/act/edit
那么打印出來的結(jié)果
你要進(jìn)行的操作是edit,你想要edit的文章id是2
可以看出無論怎么變換位置犁河,得到的結(jié)果是一樣的鳖枕。
按照變量順序綁定
鏈接 http://localhost/forum/index.php/Admin/admin/article/edit/2
那么打印出來的結(jié)果
你要進(jìn)行的操作是edit,你想要edit的文章id是2
鏈接 http://localhost/forum/index.php/Admin/admin/article/2/edit
那么打印出來的結(jié)果
你要進(jìn)行的操作是2桨螺,你想要edit的文章id是edit
因此如果采用變量順序綁定時宾符,一定要確保url上的get參數(shù)順序與操作方法上的參數(shù)順序一致。
值得注意的是按照變量名綁定僅對類似于pathinfo
方式的地址有效灭翔。如pathinfo模式
與兼容模式
魏烫。
偽靜態(tài)
偽靜態(tài)是相對于靜態(tài)頁面來說的,主要是為了更好的SEO效果肝箱,并不是真正的靜態(tài)哄褒,而是在URL的結(jié)尾添加了類似html
,htm
等的后綴煌张。在TP中默認(rèn)是開啟偽靜態(tài)的呐赡。可以通過URL_HTML_SUFFIX
來設(shè)置靜態(tài)的后綴名骏融,如:
'URL_HTML_SUFFIX'=>'shtml'
訪問:
http://localhost/forum/index.php/Admin/admin/article/2/edit.shtml
就是有效的链嘀,而訪問.html
則會報錯。也可以設(shè)置多個靜態(tài)后綴档玻,使用**|**
來進(jìn)行分割怀泊。
// 多個偽靜態(tài)后綴設(shè)置 用|分割
'URL_HTML_SUFFIX' => 'html|shtml|htm'
也可以對某些后綴進(jìn)行禁止訪問,利用URL_DENY_SUFFIX
'URL_HTML_SUFFIX' => 'html'
設(shè)置后就不能訪問任何以html
為后綴的url误趴∨恚可以將URL的模式改成rewrite
模式來配合偽靜態(tài),否則一個鏈接上既有.php
,也有.html
看上去很別扭枣申。
注意:使用偽靜態(tài)模式必須開啟httpd.conf
的mod_rewrite.so
模塊树灶。
rewire模式切換
開啟rewrite
模式需要配合修改apache
的重寫內(nèi)容。
打開httpd.conf
文件糯而,搜索mod_rewrite.so
,將該模塊前面的#
刪除泊窘。然后新建.htaccess
熄驼,放入如下代碼:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>
保存到項(xiàng)目入口文件同級目錄。重啟apache
即可烘豹。
apache的部分重寫規(guī)則
實(shí)現(xiàn)偽靜態(tài)多數(shù)是利用apache
的URL重寫規(guī)則(RewriteRule)瓜贾,我不是很懂RewriteRule
,只是從網(wǎng)上找來了一些資料携悯。
上述代碼中祭芦,RewriteEngine
表示是否開啟重寫引擎,RewriteCond
重寫應(yīng)用條件憔鬼,RewriteRule
表示重寫規(guī)則龟劲。從代碼中明顯看出,利用了正則表達(dá)式的功能轴或。
正則表達(dá)式含義
表達(dá)式 | 含義 | 表達(dá)式 | 含義 |
---|---|---|---|
昌跌? | 0-1個字符 | $ | 段落結(jié)束字符 |
. | 1個字符 | \ | 轉(zhuǎn)移字符 |
* | 0-x個字符 | ! | 取反 |
+ | 1-x個字符 | () | 內(nèi)存限定傳值 |
^ | 段落開始字符 | [0-9] | 所有數(shù)字字符 |
[a-z] | 所有小寫字母 | [A-Z] | 所有大寫字母 |
{n} | 重復(fù)n次 | {n,} | 重復(fù)n次或更多次 |
{n,m} | 重復(fù)n到m次 |
在被匹配的字符串中$N
表示與表達(dá)式中第一個()
進(jìn)行匹配照雁,上述代碼中RewriteRule
中的$1
就表示與表達(dá)式中的第一個()
進(jìn)行匹配蚕愤,這樣所有index.php/
都會被其他字符或空字符匹配。
RewriteRule
我確實(shí)不怎么懂饺蚊,網(wǎng)上的資料也很亂萍诱,就不多寫了,英語好的同學(xué)去看官方文檔吧污呼。
總結(jié)
今天關(guān)于控制器的學(xué)習(xí)就寫到這里裕坊,還有很多想要說的,但沒必要都一一贅述曙求。下次會將閱讀源碼的一些想法寫出來碍庵。最后想吐槽一下,簡書關(guān)于PHP
的程序員好像很少悟狱,每次找很長時間才能看見一篇關(guān)于PHP
的静浴,醉了醉了。