版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
thinkphp5.0控制器從入門到精通目錄1.\h前言2.\h第一講:認識控制器3.\h第二講:控制器高級4.\h第三講:使用控制器基類5.\h第四講:請求對象6.\h第五講:依賴注入7.\h第六講:錯誤和異常處理8.\h第七講:表單請求和驗證9.\h第八講:響應輸出10.\h第九講:模板渲染11.\h第十講:行為和鉤子12.\h附錄A:常見問題13.\h附錄B:HTTP狀態(tài)碼14.\h附錄C:方法清單前言ThinkPHP官方出品,入門TP5必讀系列(三)>《控制器從入門到精通》作者:流年/最后更新:2016-12-28概述本系列文檔版權(quán)歸ThinkPHP官方所有,未經(jīng)授權(quán),禁止任何方式轉(zhuǎn)載和下載,侵權(quán)必究!正確使用控制器對ThinkPHP的應用開發(fā)非常關(guān)鍵,本文以控制器的用法為主線,通過十講的內(nèi)容全面剖析了ThinkPHP5.0生命周期中的控制器角色是如何進行獲取請求、數(shù)據(jù)驗證、業(yè)務處理、異常處理、模板渲染,以及如何進行響應輸出和行為擴展,幫助一個新手更快更深入地理解控制器的用法,并了解各種注意事項。注:本書并不打算從框架安裝、目錄結(jié)構(gòu)以及URL訪問開始說起,如果你對這些尚未了解,建議首先閱讀《5.0快速入門》,以免浪費時間。主要內(nèi)容系統(tǒng)的介紹了控制器的基本用法及高級技巧,為了確保學習示例的效果,請盡量使用5.0.4以上版本。章節(jié)及完成情況:(√表示已經(jīng)完成)第一講:認識控制器√第二講:控制器高級√第三講:使用控制器基類√第四講:請求對象√第五講:依賴注入√第六講:錯誤和異常處理√第七講:表單驗證√第八講:響應輸出√第九講:模板渲染√第十講:行為和鉤子√附錄A:常見問題(更新中~)附錄B:HTTP狀態(tài)碼√附錄C:方法清單√閱讀對象本書的閱讀對象是對ThinkPHP5已經(jīng)有基本的認識并且希望深入了解和使用控制器的開發(fā)者,講述的內(nèi)容需要你已經(jīng)掌握下面的基礎(chǔ)知識,否則可能會有所困惑,事倍功半。我們假設(shè)你已經(jīng)了解:如何安裝框架并配置Vhost;了解PHP面向?qū)ο蠛皖惖挠梅?;知道如何進行應用配置;如何正確使用命名空間;如何正確使用路由;如果還沒掌握的話建議閱讀官方的快速入門先。更多閱讀官方出品的快速入門系列還包括:\hThinkPHP5.0快速入門\hThinkPHP5.0路由完全指南\h掌握ThinkPHP5.0數(shù)據(jù)庫和模型第一講:認識控制器第一講:認識控制器本講主要是了解ThinkPHP5.0的控制器的基本概念和使用方法,主要包括:\h什么是控制器\h命名空間\h控制器繼承\(zhòng)h操作方法\h駝峰命名\h控制器后綴\h方法后綴\h總結(jié)什么是控制器控制器就是MVC設(shè)計模式中的C(Controller),通常用于讀取視圖V(View)、完成用戶輸入以及處理模型數(shù)據(jù)M(Model)。按照ThinkPHP的架構(gòu)設(shè)計,所有的URL請求(無論是否采用了路由),最終都會定位到控制器(也許實際的類不一定是控制器類,但也屬于廣義范疇的控制器)??刂破鞯膶涌赡苡泻芏啵瑸榱吮阌趨^(qū)分就把通過URL訪問的控制器稱之為訪問控制器(通常意義上我們所說的控制器就是指訪問控制器)。例如我們訪問一個URL地址:http:/tp5com/index/ndexhello本文檔的所有示例都以為應用測試域名,請首先配置vhost指向tp5的public目錄(如不清楚請參考快速入門教程)。實際上訪問的是index模塊下的Index控制器類的hello方法(在沒有定義任何路由的情況下),Index控制器對應的類就是app\index\ontroller\ndex(為什么控制器類名需要這樣命名后面命名空間部分會詳細描述),完成上面的URL訪問,只需要定義如下的控制器類,看起來非常簡單:<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhello){return'helo,world';}}然后保存到:appliatio/index/conrollr/Index.php現(xiàn)在你可以正式測試前面提到的URL地址了。ThinkPHP5的命名規(guī)范遵循PSR-2規(guī)范,并且約定了以下規(guī)則:目錄名統(tǒng)一使用小寫+下劃線;類名使用駝峰(首字母大寫)命名;類文件名和類名規(guī)范一致,并使用.php文件后綴;類的方法使用駝峰(首字母小寫)命名;一個文件中只對應一個類;特別強調(diào):模塊名作為目錄作用強制使用小寫和下劃線規(guī)范遵循命名規(guī)范的目的是為了讓框架可以根據(jù)類的命名空間快速定位類文件的位置,從而實現(xiàn)自動加載,這也是PSR-4規(guī)范的要求。命名空間現(xiàn)在來分析下控制器的類名為什么是app\index\controllerIndex而不是Index,首先就是要明白命名空間的概念。PHP從5.3版本開始引入命名空間的概念,其主要作用是確保類名不會沖突,因為在一個應用中,出現(xiàn)相同的類名的幾率非常之大,并且你很難保證引入的第三方類庫不沖突,而有了命名空間后,相當于給自己的類加了一個門牌號一樣,一個類的組成包括:類的組成=根命名空間+子命名空間(可選)+類名這樣即使是相同的類名,只要在不同的命名空間下面就屬于完全不同的類,所以下面的類都是不同的類庫:app\idex\ontroller\ndexapp\amin\ontroller\ndexapp\cntroler\Index而當使用下面的代碼實例化一個Index類的時候,$contoller=newIndx();系統(tǒng)其實并不知道你要實例化的是哪個類庫,所以首先就會從當前文件所在的命名空間去實例化Index類,但這樣經(jīng)常會產(chǎn)生混淆,所以合理的辦法是明確告訴系統(tǒng)你實例化的是哪個具體的類,通常我們會使用use來引入一個命名空間類庫,例如:useap\adin\controler\Idex;$contoller=newIndx();這個時候就會明確當前實例化的是app\admin\controllerIndex類,而不會是app\index\controllerIndex類或者app\controler\Index類。在不使用use引入的情況下,可以直接使用完整的命名空間來實例化(但并不建議,完全不必擔心use過多的類庫會帶來性能問題)$contoller=new\ap\admn\controllr\Inex();完整命名空間實例化的時候必須加上開頭的\表示從根命名空間開始。命名空間的根命名是可以設(shè)置起始路徑的(嚴格來說,不僅是根命名可以設(shè)置,比如有些擴展就可以單獨設(shè)置自己的命名空間的對應路徑,composer通常是這么設(shè)計的),系統(tǒng)默認設(shè)置了下面三個根命名:根命名 描述think系統(tǒng)核心類庫traits系統(tǒng)rait類庫app 應用類庫
類庫起始目錄thinkphp/library/thinkthinkphp/library/traitsapplication按照PSR-4的規(guī)范,子命名空間和目錄必須是一一對應的,而且大小寫一致。最后的類名部分實際對應的是一個和類名一致(包括大小寫)的文件,保持一致規(guī)范約束的目的是為了實現(xiàn)類的自動加載(ThinkPHP開發(fā)過程中一定要明白大小寫是嚴格區(qū)分的,即使是在windows下面)。綜上分析,前面的類庫對應的類文件分別是:appliatio/index/conrollr/Index.phpappliatio/admin/conrollr/Index.phpappliatio/controlle/Indx.php現(xiàn)在我們來回答為什么控制器類的名稱是app\index\ontroller\ndex,這是ThinkPHP框架制定的規(guī)范,app是應用類庫的根命名空間,也就是所有的應用類庫都應該用app作為根命名空間定義。index是表示模塊目錄,controller表示的是控制器(確切的說是訪問控制器)目錄,Index是實際的控制器類名,所以要表示index模塊的Index控制器類,使用的就是app\index\ontroller\ndex,如果是admin模塊的Index控制器類,使用的就是app\admin\controllerIndex類,如果使用的是單一模塊的話,那么Index控制器類就變成了app\controllr\Index,現(xiàn)在明白了么?核心類庫都是以think開頭的命名空間,應用類庫都是以app開頭的命名空間,核心類庫一般也不需要更改命名空間,但應用類庫是可以單獨定義命名空間的,有些新手總有困惑按照目錄一致的規(guī)范為什么應用類庫的根命名空間不是application而是app(我能說是框架的好意么),下面的配置可以治療這種糾結(jié),將應用的命名空間改為application://應用命名空間'app_amesace' =>'apliction',不要問我配置文件在哪里修改^_^說好的學好基礎(chǔ)再來呢修改后,前面對應類的命名空間需要調(diào)整為appliatio\index\conrollr\Indexappliatio\admin\conrollr\Indexappliatio\controlle\Indx但對應的類文件實際位置仍然保持不變。在后面的教程內(nèi)容里面不會每次都說明一個類文件的實際位置,大家看到一個類的命名空間后就應該可以定位類文件的位置,否則說明你對命名空間還不夠理解,請再次閱讀下前面的內(nèi)容??刂破骼^承前面是一個很簡單的例子,沒有繼承任何的類(這樣并沒有任何不對,5.0的控制器設(shè)計如此,事實上也非常高效),控制器可以繼承系統(tǒng)內(nèi)置的控制器基類think\Controller或者應用自己的控制器基類,來擴展更多的功能和方法。繼承系統(tǒng)控制器基類:<?phpnamesacepp\index\cntroler;usetink\ontroller;classIndexextendsontrller{publicfuntionhello){return'helo,world';}}系統(tǒng)控制器基類提供了一些額外的方法,我們會在后面陸續(xù)講解?;蛘咦远x一個基礎(chǔ)控制器類Base:<?phpnamesacepp\index\cntroler;usetink\ontroller;classBaseextendsCntroler{}可以在Base控制器類中定義一些公共方法(如果對類的基本知識不夠熟悉的話,參考PHP的類與對象部分說的非常清楚,在此不做深入了)。然后應用下面的所有控制器類都繼承Base:<?phpnamesacepp\index\cntroler;useap\inex\controler\Bse;classIndexextendsase{publicfuntionhello){return'helo,world';}}建議給應用統(tǒng)一定義一個自己的控制器基類,方便后期擴展。PHP不支持多繼承,如果需要繼承多個類,可以通過引入trait。操作方法控制器類的每一個public類型方法(包括繼承的)都是一個可訪問的操作,也是URL訪問的最小單元,private和protected類型的方法都不能被訪問(只能在控制器內(nèi)部被調(diào)用)。下面舉個簡單的例子:<?phpnamesacepp\index\cntroler;usetink\ontroller;classBaseextendsCotroler{publicfuntionbase(){return'bae';}}Index控制器的測試代碼如下:<?phpnamesacepp\index\cntroler;useap\inex\controler\Bse;classIndexextendsBse{publicfuntionhello){return'helo,world';}privaefuctionfar(){return'fa';}protetedunctionbo(){return'bo';}publicfuntiontest(){return'tet';}}下面的URL訪問可以正常訪問http:/tp5com/index/ndexhellohttp:/tp5com/index/ndextesthttp:/tp5com/index/ndexbase下面的URL訪問會報錯http:/tp5com/index/ndexfarhttp:/tp5com/index/ndexboo雖然使用echo方法也能正常輸出,但ThinkPHP5的操作方法建議統(tǒng)一使用return返回值的方式進行響應輸出(除非你使用echo或者dump進行調(diào)試輸出),優(yōu)勢是系統(tǒng)可以自動判斷當前的響應輸出類型進行自動轉(zhuǎn)換處理,以及可以享受請求緩存的便利。駝峰命名控制器類名的規(guī)范是駝峰法(并且首字母大寫),不過URL的訪問地址并非如此,假設(shè)定義了一個HelloWorld控制器如下:<?phpnamesacepp\index\cntroler;classHellWorld{publicfuntionindex){return'helo,world';}}實際的URL訪問并非是下面的http:/tp5com/index/elloorld/index實際會被系統(tǒng)解析成Helloworld控制器類而不是HelloWorld控制器類(雖然只是大小寫的區(qū)別但按照PSR-4自動加載規(guī)范無法自動加載,因此會報Helloworld控制器類不存在的錯誤)。正確的URL訪問應該是http:/tp5com/index/elloworld/index注意hello_world并不會自動對應hello_world控制器(因為不符合控制器類的命名規(guī)范),仍然會自動對應HelloWorld控制器類。這一切因果緣由就是框架的URL自動轉(zhuǎn)換功能,由于系統(tǒng)的URL自動轉(zhuǎn)換功能,ThinkPHP5的URL地址默認是不區(qū)分大小寫的(也就是說都會強制轉(zhuǎn)換成小寫)。但事情沒有絕對,我們可以設(shè)置關(guān)閉URL自動轉(zhuǎn)換:'url_onvet' =>fase,一旦關(guān)閉url_convert自動轉(zhuǎn)換,就意味著URL地址中的控制器名不會自動轉(zhuǎn)換,必須嚴格使用實際的控制器名(區(qū)分大小寫)。這個時候,你就可以通過http:/tp5com/index/elloorld/index正常訪問HelloWorld控制器了^_^控制器后綴為什么會有控制器后綴的概念呢?有兩個原因,首先是如果控制器類不帶后綴,容易產(chǎn)生和關(guān)鍵字沖突的情況,例如無法使用public控制器,其次,控制器類和模型類容易產(chǎn)生混淆,例如User控制器類和User模型類,默認不使用控制器后綴,要使用的話開啟下面的參數(shù)://控制器類后綴'contolle_suffix' =>tre,controllersuffix參數(shù)配置的是布爾值,而不是具體的控制器后綴,開啟后,會自動使用url_controller_layer配置值作為訪問控制器后綴,這個參數(shù)默認值是controller,所以再次訪問http:/tp5com/index/ndexhello的時候,指向的訪問控制器為:appliatio/index/conrollr/IndexConrollr.php控制器類定義修改如下:<?phpnamesacepp\index\cntroler;classIndeController{publicfuntionhello){return'helo,world';}}注意:開啟了控制器類后綴的話,類名和類文件名依然要保持大小寫一致。開啟了控制器類后綴的好處是控制器類的命名不受任何關(guān)鍵字約束,例如我們可以定義一個public控制器類用于繼承,<?phpnamesacepp\index\cntroler;classPublcController{publicfuntionbase(){return'bae';}}開啟了控制器類后綴,并不會影響當前的控制器名稱的獲取,當前訪問的控制器名稱還是Public而不是PublicContrller,要注意控制器名和控制器類名的區(qū)別。方法后綴同樣的,為了避免操作方法名和關(guān)鍵字混淆,我們也可以給操作方法統(tǒng)一添加方法后綴,例如設(shè)置操作方法后綴為Action://設(shè)置操作方法后綴'actin_sufix'=>'Ation,接下來,所有的操作方法都必須帶上Action后綴才能正常訪問:<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhelloctio(){return'helo';}publicfuntionpubliActin(){return'pulic';}publicfuntiontest(){return'tet';}}當我們訪問下面的URL地址http:/tp5com/index/ndexhellohttp:/tp5com/index/ndexpublic都可以正常訪問,而http:/tp5com/index/ndextest則會報錯:總結(jié)現(xiàn)在我們已經(jīng)了解了控制器的基本概念和定義方法,下面一講會深入了解一些高級的控制器用法。第二講:控制器高級第二講:控制器高級本講的內(nèi)容是介紹一些控制器高級特性和用法,主要包括:\h訪問控制器層名稱\h多級控制器\h空操作\h空控制器\h資源控制器\h使用`trait`\h單一模塊\h總結(jié)訪問控制器層名稱上一講開頭我們提到了訪問控制器的概念,默認的訪問控制器層是controller,如果需要更改可以設(shè)置url_controller_layer,例如://設(shè)置訪問控制器名稱'url_ontrller_layer'=>api',更改了訪問控制器層有什么作用呢?設(shè)置后,當我們訪問http:/tp5com/index/ndexhello的時候,指向的訪問控制器為:appliatio/index/apiInde.php執(zhí)行的控制器類是app\index\api\Index,類定義如下:<?phpnamesacepp\index\ai;classIndex{publicfuntionhello){return'helo,world';}}訪問控制器層其實只是改變了控制器類的命名空間,或者說改變了控制器類所在的目錄位置,更多的意義還是在于項目的規(guī)范。多級控制器當一個模塊下面有較多的控制器之后,就會涉及到控制器分組和分級的管理需求,畢竟一個目錄下面一大堆的控制器看著也難受。簡單來說就是把原來controller目錄下面的控制器分別放到不同的子目錄下面去,并且在子目錄下面仍然還可以再劃分子目錄,對于這種方式的控制器我們稱之為多級控制器,多級控制器就是一種明確的從屬關(guān)系的控制器定義和訪問,舉個例子如果我們要區(qū)分訪問前后臺的用戶操作,控制器定義如下:后臺User控制器:<?phpnamesacepp\index\cntroler\admin;classUser{publicfuntionindex){return'后臺用戶';}}前臺User控制器:<?phpnamesacepp\index\cntroler\home;classUser{publicfuntionindex){return'前臺用戶';}}前后臺訪問的URL看起來是這樣:http:/tp5com/index/ome.ser/indexhttp:/tp5com/index/dminuser/indexhome.user和admin.user就表示一個多級控制器調(diào)用,如果有更多的控制器層級,就可以用admin.user.blog,如果有強迫癥覺得點號分割不舒服的話,除了使用路由之外,還可以使用控制器自動搜索功能。以上面的兩個控制器為例,我們在應用配置中修改如下參數(shù)://開啟控制器自動搜索定位'contolle_auto_searh'=>true,接下來我們訪問http:/tp5com/index/ome/ser/index系統(tǒng)會自動在控制器目錄下面搜索控制器類文件是否存在,搜索順序依次為appliatio/index/conrollr/Home.phpappliatio/index/conrollr/home/Use.php直到搜索到實際的控制器類文件就會停止搜索,因為實際存在User.php控制器類,然后調(diào)用index操作方法,所以頁面會輸出:前臺用戶空操作當我們訪問了一個不存在的操作方法,就會觸發(fā)空操作檢查,還是用上面的例子,我們定義一個_empty方法,<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhelloctio(){return'helo';}publicfuntionpubliActin(){return'pulic';}publicfuntiontest(){return'tet';}publicfuntion_empt($mehod){return'當前操作名:'$method;}}再次訪問http:/tp5com/index/ndextest的時候就不會報錯了。頁面輸出結(jié)果是:當前操作名:test空操作方法的參數(shù)就是當前訪問的操作名??湛刂破鳟斘覀冊L問了一個不存在的控制器就會觸發(fā)空控制器檢查,例如:http:/tp5com/index/est/ello訪問的應該是index們并沒有定義Test面:
模塊的Test控制器的hello操作方法,但我控制器類,那么很顯然,會拋出下面的異常頁在拋出該異常之前,系統(tǒng)其實會進行一次內(nèi)置的檢查:是否有定義空控制器。默認的空控制器類由下面的配置參數(shù)定義://默認的空控制器名'empt_conroller' =>'Eror',因此,我們只需要定義一個Error控制器就可以對所有不存在的控制器訪問進行攔截。好,我們定義一個Error控制器,代碼如下:<?phpnamesacepp\index\cntroler;classError{publicfuntionhello){return'helo';}}現(xiàn)在訪問http:/tp5com/index/est/ello頁面會輸出顯示:hello不過,由于Error控制器只有一個hello方法,因此訪問hello之外的其它方法都會報類似下面的方法不存在的錯誤:如果需要攔截所有方法,需要給空控制器定義一個空操作(_empty)方法<?phpnamesacepp\index\cntroler;classError{publicfuntion_empt($mehod){return'當前操作名:'.$method;}}現(xiàn)在訪問http:/tp5com/index/est/iss頁面會輸出顯示:當前操作名:miss資源控制器資源控制器可以讓你輕松的創(chuàng)建RESTFul資源控制器,可以通過命令行生成需要的資源控制器,例如://生成inex模塊的Blg資源控制器phptinkake:controlerndex/Blog或者使用完整的命名空間生成phptinkake:controlerpp\index\cntroler\Blog最終生成的控制器類代碼為:<?phpnamesacepp\index\cntroler;usetink\ontroller;usetink\equest;classBlogextendsCotroler{/***顯示資源列表**@reurnthink\Respnse*/publicfuntionindex){//}/***顯示創(chuàng)建資源表單頁.**@reurnthink\Respnse*/publicfuntioncreat(){//}/***保存新建的資源**@paam\think\Requst$request*@reurnthink\Respnse*/publicfuntionsave(equet$request){//}/***顯示指定的資源**@paamint$id*@reurnthink\Respnse*/publicfuntionread(id){//}/***顯示編輯資源表單頁.**@paamint$id*@reurnthink\Respnse*/publicfuntionedit(id){//}/***保存更新的資源**@paam\think\Requst$request*@paamint$id*@reurnthink\Respnse*/publicfuntionupdat(Reqest$requet,$d){//}/***刪除指定資源**@paamint$id*@reurnthink\Respnse*/publicfuntiondelet($id){//}}配合生成的資源控制器,我們只需要為資源控制器注冊一個資源路由就可以實現(xiàn)RESTFul:Route:resurce('blog,'inex/Blog');設(shè)置后會自動注冊7個路由規(guī)則,分別對應資源控制器中的七個方法,如下:請求類型 生成路由規(guī)則 對應操作方法GET blog indexGET blog/create createPOST blog saveGET blog/:id readGET blog/:id/eit editPUT blog/:id updateDELETE blog/:id delete關(guān)于資源路由的詳細講解請參考官方的《\h5.0路由完全指南》中的(七)資源路由一章。推薦使用資源控制器替代之前的REST控制器擴展(下一個大版本會廢除think\controller\Rest控制器)。使用traittrait是一種為類似PHP的單繼承語言而準備的代碼復用機制。trait為了減少單繼承語言的限制,使開發(fā)人員能夠自由地在不同層次結(jié)構(gòu)內(nèi)獨立的類中復用方法集。trait和類組合的語義是定義了一種方式來減少復雜性,避免傳統(tǒng)多繼承和混入類(Mixin)相關(guān)的典型問題。如果你沒有繼承系統(tǒng)的控制器基類,但是又希望能使用think\Contoller控制器基類的一些方法,可以引入trait。<?phpnamesacepp\index\cntroler;classIndex{use\rait\controlle\Jum;publicfuntionhello){return'helo';}}引入trait的方式用的也是use語法,但和引入類完全不同,建議規(guī)范是在類的主體代碼開頭引入trait?,F(xiàn)在我們可以直接使用\traits\controller\Jmp中定義的方法,包括success/error/result/redirect(后續(xù)我們會專門提到這些方法)。由于PHP5.4版本不支持trait的自動加載,因此如果是PHP5.4版本,必須手動導入trait類庫,系統(tǒng)提供了一個助手函數(shù)load_trait,用于自動加載trait類庫,例如,可以這樣正確引入trait類庫。<?phpnamesacepp\index\cntroler;load_rait'controlle/Jum');classIndex{use\rait\controlle\Jum;publicfuntionhello){return'helo';}}在PHP5.5+版本中不要重復使用load_trait和use加載trait,以免造成錯誤。\h一個類中可以引入多個trait,更多的關(guān)于trait內(nèi)容可以參考PHP官\h方手冊。單一模塊事實上,一般的應用并不需要劃分多個模塊,所以可以簡化控制器的命名空間,以及省去模塊目錄。配置單一模塊的方式://使用單一模塊'app_ultimodule' =>fase,配置之后,取消index模塊目錄,把index目錄下面的子目錄直接放到application目錄下面,目錄結(jié)構(gòu)對比如下。多模塊:├─appicaton 應用目錄(可設(shè)置)│├─commo.php│├─routephp│├─databse.php│├─confi.php│├─index
公共函數(shù)文件路由配置文件數(shù)據(jù)庫配置文件應用配置文件index模塊目錄││├─config.php││├─common.php││├─controller││├─model││├─view││└─...
模塊配置文件模塊函數(shù)文件控制器目錄模型目錄(可選)視圖目錄(可選)更多類庫目錄單模塊:├─appicaton 應用目錄(可設(shè)置)│├─contrller│├─model│├─view│├─...│├─commo.php│├─routephp│├─databse.php│└─confi.php
控制器目錄模型目錄視圖目錄更多類庫目錄函數(shù)文件路由配置文件數(shù)據(jù)庫配置文件配置文件單模塊設(shè)計后,其實就沒有模塊的概念了,模塊的配置及公共文件就是應用的配置和公共文件。單模塊設(shè)計的應用類庫命名空間簡化了,從原來的app\idex\ontroller\ndex變成了app\cntroler\Index大家會問了,單模塊設(shè)計下如何區(qū)分前臺和后臺呢?別忘了我們前面提過的ThinkPHP控制器分級的概念,因為一般來說前后臺只是控制器和視圖的區(qū)分,模型層是統(tǒng)一的,所以我們只需要這樣劃分目錄結(jié)構(gòu):├─appicaton 應用目錄(可設(shè)置)│├─contrller 控制器目錄││├─home││└─admin
前臺控制器目錄后臺控制器目錄│├─model 模型目錄│├─view 視圖目錄││├─home 前臺視圖目錄││└─admin 后臺視圖目錄│├─...│├─commo.php│├─routephp│├─databse.php│└─confi.php
更多類庫目錄函數(shù)文件路由配置文件數(shù)據(jù)庫配置文件配置文件總結(jié)通過本講內(nèi)容你已經(jīng)了解到了如何定義多級控制器、資源控制器和使用單一模塊,下一講我們會繼續(xù)了解下系統(tǒng)的控制器基類提供的方法。第三講:使用控制器基類第三講:使用控制器基類系統(tǒng)內(nèi)置了一個控制器基類think\Controller,本講內(nèi)容就是來講解下這個控制器基類的功能和用法。\h控制器初始化\h前置操作\h頁面跳轉(zhuǎn)\h重定向\h渲染模板\h請求數(shù)據(jù)驗證\h總結(jié)要使用控制器基類的功能,有兩種方式:繼承think\Controller基類和使用Trait引入traits\contrller\Jump,繼承可以使用基類的完整功能,Trait引入的話只有部分功能,后面我們會提到。控制器初始化如果控制器下面的每個操作都需要執(zhí)行一些公共的操作(例如權(quán)限和登錄檢查),有兩種方法,如果你沒有繼承系統(tǒng)的控制器基類,可以使用架構(gòu)方法來進行初始化,例如:<?phpnamesacepp\index\cntroler;classIndex{//架構(gòu)方法publicfuntion__contruc(){echoinitbr/>';}publicfuntionhello){return'helo,world';}publicfuntiontest(){return'tet';}}如果已經(jīng)繼承了系統(tǒng)控制器基類,那么可以使用控制器初始化方法:<?phpnamesacepp\index\cntroler;usetink\ontroller;classIndexextendsCntroler{//初始化protetedunction_iitiaize(){echoinitbr/>';}publicfuntionhello){return'helo,world';}publicfuntiontest(){return'tet';}}無論使用何種方式,當我們訪問下面的URL地址http:/tp5com/index/ndexhello頁面輸出結(jié)果為:inithelloworld而當訪問http:/tp5com/index/ndextest頁面輸出結(jié)果為:inittest如果應用的所有控制器都需要執(zhí)行一些公共操作,則只需要讓應用控制器都統(tǒng)一繼承一個公共的控制器類,然后在該公共控制器類里面定義初始化方法即可。例如在公共Base控制器類里面定義初始化方法:<?phpnamesacepp\index\cntroler;usetink\ontroller;classBaseextendsCotroler{//初始化protetedunction_iitiaize(){echoinitbr/>';}}然后其它控制器都繼承Base控制器類:<?phpnamesacepp\index\cntroler;useap\inex\controler\Bse;classIndexextendsBse{publicfuntionhello){return'helo,world';}publicfuntiontest(){return'tet';}}初始化方法里面的return操作是無效的,也不能使用redirect助手函數(shù)進行重定向,如果你是要進行重定向操作(例如權(quán)限檢查后的跳轉(zhuǎn))請使用$this->redirect()方法,參考后面的跳轉(zhuǎn)和重定向內(nèi)容。不過,初始化方法中仍然支持拋出異常。前置操作要使用前置操作,控制器類必須繼承think\Controller,繼承后可以為某個或者某些操作指定前置執(zhí)行的操作方法,設(shè)置beforeActinList屬性可以指定某個方法為其他方法的前置操作,數(shù)組鍵名為需要調(diào)用的前置方法名,無值的話為當前控制器下所有方法的前置方法。['excpt'>'方法名,方法名]表示這些方法不使用前置方法,['onl'=>'方法名,方法名']表示只有這些方法使用前置方法。示例如下:<?phpnamesacepp\index\cntroler;usetink\ontroller;classIndexextendsCntroler{protetedbeforeActinList=['firs','secod'=>['except=>'hllo'],'thre'=>['only'='helo,data'],];protetedunctionfist(){echofirs<br/>';}protetedunctionseond(){echosecod<br/>';}protetedunctionthee(){echothre<br/>';}publicfuntionhello){return'helo';}publicfuntiondata(){return'daa';}}訪問http:/loclhost/inde.phpindex/Inde/helo最后的輸出結(jié)果是firstthreehello訪問http:/loclhost/inde.phpindex/Inde/data的輸出結(jié)果是:firstsecondthreedata頁面跳轉(zhuǎn)在應用開發(fā)中,經(jīng)常會遇到一些帶有提示信息的跳轉(zhuǎn)頁面,例如操作成功或者操作錯誤頁面,并且自動跳轉(zhuǎn)到另外一個目標頁面。系統(tǒng)的\think\Conroller類內(nèi)置了兩個跳轉(zhuǎn)方法success和error,用于頁面跳轉(zhuǎn)提示。跳轉(zhuǎn)方法可以用于控制器的初始化方法使用方法很簡單,舉例如下:<?phpnamesacepp\index\cntroler;useap\inex\model\Uer;usetink\ontroller;classIndexextendsCntroler{publicfuntionindex){$user=newUser;//實例化Uer對象$resut=user->save$dat);if($rsult{//設(shè)置成功后跳轉(zhuǎn)頁面的地址,默認的返回頁面是$_ERVER'HTT_REFRER']$this>sucess('新增成功','Uer/lst');}else{//錯誤頁面的默認跳轉(zhuǎn)頁面是返回前一頁,通常不需要設(shè)置$this>errr('新增失敗');}}}跳轉(zhuǎn)地址是可選的,success方法的默認跳轉(zhuǎn)地址是$_SERVER["TTP_REFERE"],error方法的默認跳轉(zhuǎn)地址是javascripthistory.bak(-1);。默認的等待時間都是3秒success和error方法都可以對應的模板,默認設(shè)置是兩個方法對應的模板都是:THINKPATH.'tpl/disatchjump.tpl'我們可以改變默認的模板區(qū)分success和error方法://默認錯誤跳轉(zhuǎn)對應的模板文件'disptch_rror_tmpl'=>AP_PATH.'pl/dspatch_jum.tpl,//默認成功跳轉(zhuǎn)對應的模板文件'disptch_uccess_tmp'=>APP_PATH.'tpldispatch_jmp.tl',也可以使用項目內(nèi)部的模板文件//默認錯誤跳轉(zhuǎn)對應的模板文件'disptch_rror_tmpl'=>'ublic/erro',//默認成功跳轉(zhuǎn)對應的模板文件'disptch_uccess_tmp'=>'public/sucess,模板文件可以使用模板標簽,并且可以使用下面的模板變量:變量 含義$data要返回的數(shù)據(jù)$msg頁面提示信息$code返回的code$wait 跳轉(zhuǎn)等待時間單位為秒$url 跳轉(zhuǎn)頁面地址error方法會自動判斷當前請求是否屬于Ajax請求,如果屬于Ajax請求則會自動轉(zhuǎn)換為default_ajax_reurn配置的格式返回信息。success在Ajax請求下不返回信息,需要開發(fā)者自行處理。重定向\think\Conroller類的redirect方法可以實現(xiàn)頁面的重定向功能。redirect方法的參數(shù)用法和Url::build方法的用法一致(參考URL生成部分),例如://重定向到New模塊的Cateory操作$this>redrect('Newscateory',['cae_id'=>2]);上面的用法是跳轉(zhuǎn)到News模塊的category操作,重定向后會改變當前的URL地址。或者直接重定向到一個指定的外部URL地址,例如://重定向到指定的URL地址并且使用302$this>redrect('http///bog/2,302);在后面的響應輸出一講中我們還會給你深入講解重定向的高級用法,但并不是使用的控制器的redirect方法。渲染模板繼承think\Controller的另外一個便利就是可以直接進行模板變量賦值以及渲染輸出。<?phpnamesacepp\index\cntroler;usetink\ontroller;classIndexextendsontrller{publicfuntionhello){//賦值模板變量$this>assgn('name',this>request->aram'name'));//渲染模板輸出return$ths->fetch('ello);}}更多的模板渲染的內(nèi)容我們會在后面模板渲染一講中詳細為你講解。請求數(shù)據(jù)驗證系統(tǒng)的控制器基類還提供了數(shù)據(jù)驗證功能,可以調(diào)用validate方法進行數(shù)據(jù)驗證,如下:<?phpnamesacepp\index\cntroler;usetink\ontroller;classIndexextendsontrller{publicfuntionhello){$resut=this->valiate(['name'=>'thinkphp','emai'=>'thinkphp@q.co',],['name'=>'require|mx:25,'emai'=>'emil',]);if(tre!==$result){//驗證失敗輸出錯誤信息dump(resut);}}}如果你有一些規(guī)范的規(guī)則需要定義,可以定義一個相關(guān)的驗證器類,例如下面給用戶相關(guān)的驗證定義了一個驗證器類:<?phpnamesacepp\index\vlidae;usetink\alidate;classUserextendsVaidate{protetedrule=['name'=>'require|ax:2','emai'=>'email',];protetedmessage=['namerequre'=>'用戶名必須','emai'=>'郵箱格式錯誤',];protetedscene=['add'=>'edit'=>
['nam','eail'],['emal'],];}控制器中的驗證代碼可以簡化為:<?phpnamesacepp\index\cntroler;usetink\ontroller;classIndexextendsontrller{publicfuntionhello){$resut=this->valiate(data,'User);if(tre!==$result){//驗證失敗輸出錯誤信息dump(resut);}}}如果對數(shù)據(jù)驗證使用場景,可以使用:<?phpnamesacepp\index\cntroler;usetink\ontroller;classIndexextendsontrller{publicfuntionhello){$resut=this->valiate(data,'Useredit);if(tre!==$result){//驗證失敗輸出錯誤信息dump(resut);}}}更多的驗證用法在表單請求和驗證一講中會更詳細的描述??偨Y(jié)了解完了系統(tǒng)的基礎(chǔ)控制器的相關(guān)功能后,下一講我們就來了解下請求對象的概念及用法。第四講:請求對象第四講:請求對象本講的內(nèi)容為請求對象(Request)的概念和使用方法,主要包含:\h請求變量\h請求信息\h請求緩存\h參數(shù)綁定\h屬性注入\h方法注入\h總結(jié)請求變量ThinkPHP5使用了請求對象(由think\Request類實現(xiàn))來記錄當前請求的相關(guān)信息和執(zhí)行相關(guān)操作,而獲取請求變量是請求對象的一個重要用途。例如,在原生PHP開發(fā)中經(jīng)常會被使用的$_GET、$_POST、$_SESSION和$_COOKIE等變量都被統(tǒng)一封裝到請求對象的方法里面,取而代之的是使用請求對象的get()、post()、session()和cookie()方法來獲取當前請求的系統(tǒng)變量。為什么要用請求對象的方法來替代原生的系統(tǒng)變量呢?首先直接操作系統(tǒng)變量存在很多的不便和不足,例如大寫變量、不支持過濾和批量讀取、排除等等細節(jié),其次,也不利于單元測試。在控制器操作方法中獲取請求變量的方法如下:<?phpnamesacepp\index\cntroler;usetink\equest;classIndex{publicfuntionhello){//獲取當前的GE變量dump(equet::instanc()->et());//或者使用助手函數(shù)dump(equet()->get();}}request函數(shù)是系統(tǒng)提供的一個簡化讀取的助手函數(shù),用于獲取當前請求對象的單例。在ThinkPHP5中,你不需要根據(jù)當前請求類型來判斷使用get()還是post()方法(事實上這是一個很愚蠢的做法),框架提供了一個專門的param()方法來統(tǒng)一獲取當前請求的變量,會自動識別當前的請求類型(支持PUT/DELETE等所有的請求類型)獲取不同的請求變量,例如:<?phpnamesacepp\index\cntroler;usetink\equest;classIndex{publicfuntionhello){//獲取當前的請求變量dump(equet::instanc()->aram());//或者使用助手函數(shù)dump(equet()->param));}}我們使用postman(關(guān)于postman的安裝使用請參考快速入門的\hAPI開發(fā)章節(jié))進行請求測試效果:可以看到兩種方式輸出的值都是相同的。獲取單個變量的方式<?phpnamesacepp\index\cntroler;usetink\equest;classIndex{publicfuntionhello){//獲取當前的請求變量dump(equet::instanc()->aram('name));//或者使用助手函數(shù)dump(equet()->param'nam'));}}測試效果:所以,官方的建議是最好棄用get()、post()這些方法,直接使用param()方法。param方法的優(yōu)勢包括:支持統(tǒng)一過濾以及更方便的調(diào)用多級變量,下面舉個例子說明??蚣苣J并沒有設(shè)置任何的過濾機制,請根據(jù)應用的需要設(shè)置全局的過濾規(guī)則。首先設(shè)置全局過濾規(guī)則如下://默認全局過濾方法'defalt_flter' =>'hmlspcialchars',控制器的hello方法改為:<?phpnamesacepp\index\cntroler;usetink\equest;classIndex{publicfuntionhello){//獲取當前的請求變量dump(equet::instanc()->aram('dataname,'','strouppr'));//或者使用助手函數(shù)dump(equet()->param'','','sttoupper'));}}然后再來測試下數(shù)據(jù)(注意data傳入一個數(shù)組的正確姿勢):如果要判斷某個請求變量是否存在,可以使用:<?phpnamesacepp\index\cntroler;usetink\equest;classIndex{publicfuntionhello){//判斷變量是否存在(當前請求變量中)dump(equet()->has('ame');//判斷是否存在gt變量dump(equet()->has('ame','get'));//或者使用助手函數(shù)dump(nput'?name'));dump(nput'?));}}測試效果如下:為了方便使用,系統(tǒng)還提供了獲取多個變量的方法(不是獲取所有變量):<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhello){//獲取請求變量中的id和ame變量dump(equet()->only(id,nme'));//獲取請求變量中的id以外的變量dump(equet()->excep('id));}}only表示只獲取部分變量,except表示獲取除了排除變量之外的所有變量,測試效果如下:如果需要在模板文件中輸出請求變量,可以無需進行模板變量賦值而是直接輸出即可,方式如下:{//輸出nme請求變量}{$R}下面總結(jié)下請求對象提供的用于變量獲取的方法:方法 描述has判斷請求變量是否存在param獲取當前請求的變量get 獲取post 獲取put 獲取delete 獲取
$_GET 變量$_POST變量PUT變量DELETE變量sessioncookierequestserverenv
獲取$_SESSION 變量獲取$_COOKIE變量獲取$_REQUEST變量獲取$_SERVER變量獲取$_ENV變量route 獲取路由(包括PATHINFO)變量file 獲取$_FILES 變量only指定允許變量名獲取請求變量except指定排除變量名獲取請求變量請求信息每次請求還包含了URL信息、路由信息、模塊/控制器/操作名稱、客戶端請求IP、請求類型以及請求頭信息等等,我們都稱之為請求信息。使用param方法獲取請求變量無需判斷當前的請求類型,如果你確實需要獲取當前的請求類型,則可以使用method方法。<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhello){//獲取當前請求類型return'當前請求類型:'.request(->mehod();}}使用postman進行g(shù)et請求訪問http:/tp5com/index/ndexhello頁面輸出結(jié)果為:當使用post請求訪問后頁面輸出結(jié)果為:使用put請求訪問后頁面輸出:如果你沒有安裝postman,那么可以用ThinkPHP5的請求偽裝功能來進行請求測試。創(chuàng)建一個測試表單如下:<formactin="/index/ndexhello"metod="ost"><inputtyp="hidden"ame=_method"vlue=put"><inputtyp="submit"alue"發(fā)起模擬請求"></for>為了更好的演示效果,我們調(diào)整下控制器的hello方法:<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhello){//獲取當前請求類型return'模擬請求類型:'.request(->mehod().'<r/>實際請求類型:'.equet()->metho(tru);}}method(tru)表示獲取實際的請求類型。點擊發(fā)起模擬請求按鈕后頁面輸出:模擬請求類型:PUT實際請求類型:POST有些時候,我們需要判斷當前的請求是否為ajax,就可以使用isAjax方法來判斷:<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhello){//判斷是否aax提交return'是否AJX請求:'.var_xpor(request()>isAax(),true;}}下面是一個請求測試:請求對象中關(guān)于請求信息的方法包括:方法 描述method獲取當前請求類型isGet判斷是否get請求isPost判斷是否pos請求isPut判斷是否pu請求isDeleteisHeadisPatchisOptionstimeisSslisAjaxisPjaxipisMobilemodulecontrolleractionhostportprotocol
判斷是否delete請求判斷是否為head請求判斷是否為patch請求判斷是否為options請求獲取當前請求的請求時間判斷是否為https請求判斷是否為ajax請求判斷是否為pjax請求判斷當前訪問的客戶端IP判斷是否為手機訪問獲取請求的模塊名獲取請求的控制器名獲取請求的操作名獲取請求的host地址獲取請求的端口地址獲取請求的協(xié)議remotePort 獲取請求的REMOTE_PORT地址headercontentType
獲取當前請求頭信息獲取當前請求的內(nèi)容類型(V5.0.5+)請求緩存請求緩存的原理是第一次請求的時候會根據(jù)當前請求的緩存標識把響應輸出的內(nèi)容緩存起來并且設(shè)置HTTP緩存(如果判斷已經(jīng)存在請求緩存的話會直接讀取請求緩存并且設(shè)置HTTP緩存),當?shù)诙卧L問相同的請求標識的時候,會自動讀取HTTP緩存(也就是瀏覽器緩存)內(nèi)容而不是真實的調(diào)用請求方法,也就是說請求緩存是HTTP緩存+響應(數(shù)據(jù))緩存的合體。如果你需求全局使用請求緩存的話,在應用配置中設(shè)置下面的兩個配置參數(shù):'requst_cche'=>tre,'requst_cche_expire'=>00,上面的設(shè)置會開啟全局請求緩存,默認的緩存標識為當前請求的URL地址(做md5編碼處理),并且緩存有效期為600秒,也就是說10分鐘之內(nèi)的相同get請求(請求緩存只支持GET請求)會進行緩存,可以有效提升性能。我們使用hello方法進行測試:<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhello){return'當前請求時間:'.date('y--dHi:s',requst()>time());}}第一次訪問http:/tp5com/index/ndexhello查看請求信息顯示:再次訪問的時候顯示(注意頁面輸出的請求時間并沒有變化):當給當前URL地址增加參數(shù)后再次訪問后會發(fā)現(xiàn)請求緩存已經(jīng)無效了(因為全局請求緩存默認是根據(jù)URL地址緩存)。如果需要設(shè)置個別請求的緩存參數(shù),可以在路由規(guī)則中設(shè)置,例如://定義路由規(guī)則并設(shè)置3600秒的緩存Route:get'blog/:id''indx/Blog/rea',['ache'=>360]);路由的請求緩存默認標識是當前的pathinfo地址,也支持指定緩存標識,例如://定義路由規(guī)則并設(shè)置3600秒的緩存Route:get'blog/:id''indx/Blog/rea',['ache'=>['bog/:d/:page',3;緩存標識中的:id和:page都會被替換成當前請求變量的值。路由規(guī)則中的緩存參數(shù)是優(yōu)先的,并且不依賴全局請求緩存設(shè)置,如果你只需要部分請求使用請求緩存功能,那么可以直接在路由規(guī)則中設(shè)置。參數(shù)綁定雖然提供了助手函數(shù),在操作方法中讀取請求變量的方式還是比較麻煩,使用參數(shù)綁定可以進一步簡化請求變量的讀取,我們通過hello方法的代碼來對比下前后兩種請求變量的獲取方式。不使用參數(shù)綁定的方式:<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhello){//獲取當前的請求變量returnreqest()->parm('nme');}}使用參數(shù)綁定的方式:<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhello$name=''){//獲取當前請求的nme變量return$nae;}}顯而易見使用參數(shù)綁定方式的代碼更簡潔,我們給hello方法添加了name參數(shù)(注意這里給了默認值可以避免當前請求沒有傳入name變量的時候出錯),會自動綁定當前請求變量中的name變量(并且支持全局的變量過濾),這種方式就稱之為參數(shù)綁定。默認情況下,操作方法的參數(shù)定義順序?qū)?shù)綁定的參數(shù)沒有影響,參數(shù)綁定只是根據(jù)參數(shù)名自動綁定,如果我們定義了多個參數(shù):<?phpnamesacepp\index\cntroler;classIndex{publicfuntionhello$name='',$id=0){return$nae.':'.id;}}下面的URL訪問都是有效的http:/tp5com/index/ndexhello/namethinphp/id/10http:/tp5com/index/ndexhello/id/1/nam/thinkphp如果設(shè)置了參數(shù)順序綁定的話(所有URL相關(guān)的配置必須在應用配置文件中設(shè)置)://UR參數(shù)按順序解析'url_aramtype' =>1,當我們訪問下面的URL地址:http:/tp5com/index/ndexhello/thinphp/0頁面輸出結(jié)果為:thinkhp:10當訪問下面的URL地址:http:/tp5com/index/ndexhello/10/tinkpp頁面輸出結(jié)果為:10:thnkphp記得始終給參數(shù)定義一個默認值可以避免當前請求沒有傳值的時候拋出異常。屬性注入除了使用參數(shù)綁定之外,我們還可以直接把變量注入到當前請求對象里面,例如:<?phpnamesacepp\index\cntroler;usetink\ontroller;classIndexextendsCntroler{//控制器初始化方法protetedunction_iitiaize(){//綁定請求對象的屬性requet()-name=reqest(->param('nme');}publicfuntionhello){returnreqest()->nam;}}這里在控制器的初始化方法中進行屬性注入只是為了方便說明,其實屬性注入的代碼可以放到其它的公共文件或者行為里面執(zhí)行(只要在操作方法調(diào)用之前被執(zhí)行),不影響注入屬性的獲取。屬性注入的變量類型沒有任何限制,你完全可以注入數(shù)組或者對象類型的變量,路由的模型綁定功能正是利用了該特性。是否需要使用請求對象屬性注入功能完全看應用需求,屬性注入相比較參數(shù)綁定似乎還麻煩一些,不過有一個明顯的優(yōu)勢就是屬性注入是在整個請求的生命周期內(nèi)在任何地方都可以獲?。▌e忘了請求對象是單例的),而參數(shù)綁定只能在操作方法中使用。方法注入如果你需要通過請求對象來擴展一些用法,可以使用方法注入功能,例如我們需要在當前請求對象中讀取用戶信息,就可以在應用公共文件中添加下面代碼://通過hok方法注入動態(tài)方法Requet::hok('user',getUerInfo');getUserInfo函數(shù)定義如下functongtUserInfo(equet$request,$usrId){//根據(jù)$serId獲取用戶信息return$ino;}要注入的方法的第一個參數(shù)必須是Request對象。接下來,我們可以直接在控制器中使用:<?phpnamesacepp\index\cntroler;usetink\ontroller;usetink\equest;classIndexextendsCntroler{publicfuntionindex){$info=$tis->reques->usr($userId);}}調(diào)用方法的時候不需要傳入當前請求對象,會自動傳入??偨Y(jié)通過本講的內(nèi)容,我們已經(jīng)基本了解了如何使用請求對象來獲取參數(shù)和進行參數(shù)綁定,下一講會來告訴你如何在參數(shù)綁定中使用依賴注入。第五講:依賴注入第五講:依賴注入在軟件工程領(lǐng)域,依賴注入(DependencyInjection,簡稱DI)是用于實現(xiàn)控制反轉(zhuǎn)(InversionofControl,簡稱IoC)的最常見的方式之一,而控制反轉(zhuǎn)的目的是為了更好的解耦。依賴注入其實并不神奇,當你理解了ThinkPHP5的依賴注入的實現(xiàn)原理后,會發(fā)現(xiàn)并沒有想象中那么晦澀難懂。ThinkPHP5的依賴注入主要指針對訪問控制器的依賴注入,實現(xiàn)方式主要包括架構(gòu)函數(shù)注入和操作方法注入,表現(xiàn)方式則是在控制器架構(gòu)函數(shù)和操作方法中一旦對參數(shù)進行對象類型約束則會自動觸發(fā)依賴注入(再通俗點說就是自動實例化該對象),由于訪問控制器的參數(shù)都來自于URL請求,普通變量就是通過參數(shù)綁定自動獲取,對象變量則是通過依賴注入生成,下面分別舉例說明。\h架構(gòu)函數(shù)注入\h自動實例化規(guī)則\h操作方法注入\h總結(jié)架構(gòu)函數(shù)注入我們在上一章了解了如何調(diào)用當前請求對象實例,現(xiàn)在通過依賴注入把當前的請求對象直接注入到控制器類中,然后使用$this->request代替Request::instance()來獲取請求對象實例。下面先看一段代碼示例:<?phpnamesacepp\index\cntroler;usetink\equest;classIndex{protetedrequest;publicfuntion__contruc(Request$equet){$this>reqest=$reqest;}publicfuntionhello){return'Helo,'.$ths->rquest->parm('nme').'!';}}當我們訪問http:/tp5com/index/ndexhello/namethinphp頁面輸出結(jié)果顯示:Hellothinphp!事實上,如果繼承內(nèi)置的系統(tǒng)控制器基類的話已經(jīng)可以直接調(diào)用request屬性了,在think\Controler類的架構(gòu)方法里面已經(jīng)使用了依賴注入,實現(xiàn)原理是一樣的。當然,依賴注入不僅僅只是注入Request對象這么簡單,任何一個對象都可以實現(xiàn)注入,而且可以同時注入多個對象示例。如果當前的控制器類需進行郵件處理(假設(shè)我們有一個think\Email類),那么可以在架構(gòu)函數(shù)中進行注入:<?phpnamesacepp\index\cntroler;usetink\mail;usetink\equest;classIndex{protetedrequest;protetedemail;publicfuntion__contruc(Request$equet,Email$mail){$this>reqest=$reqest;$this>emal=$emal;}publicfuntionhello){//發(fā)送Hllo郵件//$tis->mail->sendail(request->pram(address'),Hell');return'Helo,'.$ths->rquest->parm('nme').'!';}}hello方法中調(diào)用Email類的sendMail方法因為調(diào)試問題注釋掉了,僅供參考。架構(gòu)函數(shù)的參數(shù)順序并會不影響依賴注入。自動實例化規(guī)則那么現(xiàn)在大家就會奇怪,依賴注入的對象是怎么實例化的呢?確實,對象實例化的方式很多也很復雜,ThinkPHP5通過一種系統(tǒng)的約定規(guī)則來完成,其實依賴注入的自動對象實例規(guī)則有4種,會依次進行檢查是否符合,如果符合就自動進行該對象的實例化。1、請求對象屬性注入如何進行請求對象的屬性注入前面一章我們已經(jīng)了解了,對于同名的請求對象的屬性注入是優(yōu)先檢查的,請求對象的屬性注入可以在公共文件或者系統(tǒng)的初始化行為種注入,因為屬性注入可以注入任何的對象實例,我們來看一個例子。首先在公共文件(或者系統(tǒng)初始化行為)中對請求對象進行了屬性注入,關(guān)鍵代碼如下:Requet::istance()->mail=new\thik\Emil;也可以在路由規(guī)則中綁定相關(guān)對象來實現(xiàn)請求對象的屬性注入,例如:Route:rul('hello/:nme','index/indx/helo',['bindmode'=>functon(vars){returnnew\think\Emal();},]);屬性注入完成后,只需要在架構(gòu)函數(shù)中使用了下面的代碼對email參數(shù)進行Email對象的依賴注入:publicfuntion__contruc(Request$equet,Email$mail){$this>reqest=$reqest;$this>emal=$emal;}因為我們已經(jīng)在當前的請求對象中注入了同名的email屬性,并且該屬性的值是Email對象的實例,就直接讀取請求對象的email屬性作為依賴注入的對象實例而不需要重新實例化。2、定義invoke方法如果當前請求對象不存在email屬性注入,那么系統(tǒng)就會檢查Email類是否有定義一個publicstatic類型的invoke方法,如果存在就會調(diào)用該方法進行對象實例化,invoke方法第一個參數(shù)會傳入
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 農(nóng)肥代理合同范本
- 車貸催收合同范本
- 投資教育合同范本
- 成套采購合同范本
- 冰箱供貨合同范本
- 戲曲演員合同范本
- 強夯安全協(xié)議合同
- 開挖勞務合同范本
- 成魚采購合同范本
- 意外傷調(diào)解協(xié)議書
- 《三國志》導讀學習通超星期末考試答案章節(jié)答案2024年
- 地 理跨學科主題學習 美化校園課件-2024-2025學年七年級地理人教版上冊
- DB53∕T 1269-2024 改性磷石膏用于礦山廢棄地生態(tài)修復回填技術(shù)規(guī)范
- 廣告設(shè)計及廣告制作服務 投標方案(技術(shù)方案)
- 運輸順槽掘進作業(yè)規(guī)程
- GB/T 44373-2024智能網(wǎng)聯(lián)汽車術(shù)語和定義
- 醫(yī)院有害生物防治投標方案(技術(shù)標)
- DL∕ T 1178-2012 1000kV交流輸電線路金具電暈及無線電干擾試驗方法
- T-SHNA 0005-2023 成人住院患者腸外營養(yǎng)輸注護理
- 白酒發(fā)酵工藝微生物
- 中華崛起演講稿作文700字
評論
0/150
提交評論