這裡不詳細講解何為MVC模式,只是簡單介紹,關於MVC的具體資訊可以去網路上找尋,MVC模式在我理解來它將一個項目分解成三部分,分別是Model(模型),View(視圖),Controller(控制器),這三個單詞的縮寫組合即為MVC.MVC是一種普遍的軟體敏捷開發模式,在許多領域特別是案頭編程領域早已經得到了廣泛的應用,然而在像php一樣的指令碼語言中比較難以實現,特別是幾年前在指令碼語言中很難看到MVC的實現,但是今年隨著眾多架構的湧現,MVC在各個架構中得到了初步實現,其他架構中的實現方式暫且不提,這裡只是介紹codeigniter是如何?MVC的.
關於單一入口
單一入口指在一個網站(應用程式)中,所有的請求都是指向一個指令檔的,例如CI中的http:\\localhost\index.php,所有對應用程式的訪問都是必須通過這個入口,正是單一入口才使得MVC模式得以實現,因為當你訪問index.php的時候,應用程式會做大量的初始化工作,調用大量的基礎類庫,並根據index.php後面的參數載入控制器,然後載入試圖,模型等內容資訊.
ci的所有檔案載入都要經過控制器調用,因為控制器是CI中的超類,也就是其他的類都依附於它,所以用單一入口方式訪問CI應用程式的時候,需要在index.php的後面加上控制器名和控制器中的方法名,如果你對於此沒有任何概念或者無法理解,可以去CI的官方網站下載它的官方文檔,然後詳細瞭解它的工作方式
CI的官方文檔非常詳盡易懂,這裡描述的是文檔上所不存在的基本原理部分.
開始
或許應該先講解CI的控制器是如何工作的,CI中的一個控制器就是使用者編寫的一個類,它繼承自系統的Controller類,例如假設我們要構建一個可以通過http:\\localhost\index.php\control\func\m1\m2訪問的頁面,我們需要做哪些工作呢,首先我們要在system\application\controllers\檔案夾下建立一個檔案contro.php檔案,這個檔案即是我們要訪問的控制器類所在檔案,在此檔案中建立以下內容:
1 class Controller extends Controller 2 3 nController()45parent::Controller(); 6 7 8 n func($m1,$m2)910$this->load->model("MSomemodel","",TRUE);11$data["data1"]=$this->MSomemodel->getvalue();12$this->load->view("welcome",$data);131415
這並不是一個控制器的基本組成部分,而是包含了model和view的一個控制器例子,
首先注意控制器的類名應該是首字母大寫的,然後在類的建構函式裡應該調用父類的建構函式,之後則是func()方法,也就是url後面所帶參數的第二個部分,這個方法帶有兩個參數,這兩個參數的值就是url的第三部分和第四部分的值,也就是單一入口的訪問方式實際是:http:\\localhost\index.php\控制器名\方法名\方法的參數1\方法的參數2\......
在控制器類中每個方法代表一個頁面,也就是可以將很多類似的操作放到一個控制器中,實現對操作的統一
在上述的例子中的func()方法中的其他部分分別載入了model和view,載入model的時候載入的是在models檔案夾中的msomemodel.php檔案中的MSomemodel類,這個類負責應用程式的模型部分,也就是負責資料的交換,例如資料庫的儲存.
然後我們通過$data=$this->MSomemodel->getvalue()執行了model中的一個方法,並從這個方法返回了資料,然後賦值給$data["data1"],$data是一個關聯陣列,我們通過這個數組向view視圖檔案傳值,而不是使用常見的模板模式,這種方法更好地分離了MVC各個部分的處理,同時在效能方面有其獨特的一面.
之後我們通過將$data數組傳給views檔案夾中的welcome.php檔案,這個檔案是常規的php和html混寫的指令碼,在這個指令碼中可以利用傳過來的$data數組輸出資訊,但是注意在view檔案中輸出資訊的時候不必使用$data["data1"],而只需要echo$data1;即可.
基本的工作方式就是這樣的,下面從代碼層級來分析實現
程式碼分析
在CI中將Controller類作為超類來處理,也就是所有載入MVC實現模式的進程都從Controller類開始,所以我們忽略CI在載入到這個類的時刻前面的執行過程,而直接從Controller類所在的檔案開始分析.
Controller類所在的檔案位於system/libraries/Controller.php檔案中.
在這個類中首先載入了所有必須的基礎類,包括:"Config","Input", "Benchmark","URI","Output", "Language","Router"類.之後載入Roader類並執行了它的_ci_autoloader()方法,這個類是MVC模式的核心,控制器中所有其他內容的載入都是通過它實現的,下面對其代碼進行分析:
首先來看_ci_autoloader()方法,這個函數實現了自動載入某些類庫或者類,如果在你的應用程式中總是要用到某些類,但是你又不確保在CI中是否已經自動載入了這些類的話,你可以在config/autoload.php檔案中設定要自動載入的library或者helper或者plugin的數組.具體請參考手冊.
首先看看CI是如何載入libraries的,這個方法允許你在你的控制器的任何地方(通常是建構函式裡)使用$this->load->library("name");來載入某個類,這個類可以是使用者自訂的類也可以是系統的類庫,使用者自訂的類需要遵循CI的約定,具體資訊見手冊中的"建立你自己的類庫"部分.library()方法以一個字串或者一個類庫名稱的數組作為第一個參數,之後的處理將遍曆然後載入所有的類,你可以通過第二個參數向要載入的類的建構函式傳遞參數,第三個參數允許你定義返回的對象的名稱,後面的兩個參數通常不使用,這個方法在簡單判斷了參數是否為空白之後調用了方法_ci_load_class($class,$ms= NULL, $t_name=NULL),這是一個非常複雜的函數,這個類載入第一個參數所指定的類,在這個類中進行了複雜的路徑判斷之後找到了所需要的類檔案之後,調用了方法_ci_init_class($class,"",$ms,$t_name);這個類用來執行個體化一個類,如果在載入這個類的語句中包含了上述的第三個參數,則返回一個執行個體,以這個參數作為執行個體名,如果沒有設定第三個參數,則返回一個以類名命名的執行個體名,這也是為什麼前面的例子中在載入了model之後,直接將model類名作為一個對象使用的原因.
之後我們來看CI是如何載入模型的,這個方法允許你在控制器中使用$this->load->model($modelname,$name,$db_conn)載入模型,這三個參數分別是載入的模型的名稱,載入後執行個體化的對象名稱,是否自動連接資料庫.後面兩個參數可以省略,你可以講多個模型一次載入,只需要將第一個參數設定成數組即可,這個方法首先將傳過來的第一個參數以"\"分解成數組,這種機制允許你在模型中建立多層檔案夾,更加合理地安排代碼的分組,之後程式取出數組的最後一個元素作為要載入的類的名稱,並根據路徑尋找此類,之後包含此檔案,並執行個體化此類,如果設定了第二個參數,則執行個體化到$name的對象中,否則預設以類名作為對象名進行執行個體化.
再來看CI是如何載入視圖的,view($view, $vars = array(), $return=FALSE)方法的第一個參數是要載入的視圖名稱,第二個參數是要傳給視圖的變數值,第三個參數指定是否返回輸出緩衝區的資料.這個方法將所有數組作為一個數組參數調用了_ci_load($_ci_data)方法,這個方法將傳過來的變數數組通過extract()函數解析成符號表(也就是將鍵名當作變數名,值作為變數的值),並將這些變數緩衝起來,以便可以再不同的視圖中能夠互相交流變數,也就是這個方法允許調用多次,為了在每次調用時都能自動載入已經傳給前面視圖的變數,將所有傳給視圖的變數都緩衝在類的一個屬性中,這樣每次調用方法的時候都會擷取所有的變數.之後載入這個視圖檔案,然後將其作為輸出緩衝的一部分賦給全域變數$OUT,這個變數用來控制緩衝輸出,這樣做可以提高效率以及可以使調試的時間更準確.
其他的載入方法和上述的方法原理基本相同,只是根據情況有少許改變,CI在實現MVC模式的方法中將所有的檔案都包含在控制器中,我們在包含了這些檔案後,可以再控制器中自由使用這些對象和資料,然後最後通過緩衝輸出類來輸出所有的資料,雖然Loader這個類的結構看起來很複雜,但是其實它的實現時很簡單的,其內部的代碼原理基本相同,而且清晰明了,仔細看的話不難理解.