1.4 自定义框架
那么,就先從實現MVC框架模式開始。
1.4.1 MVC框架模式的實現
Yii使用了 Web 開發中廣泛采用的MVC框架模式,因此使用者在使用Yii建立應用系統時,必須對MVC的原理有一些了解。MVC一直以來是Yii框架初學者很難跨過的一個障礙,本節仿照Yii框架代碼實現了MVC的軟件架構,希望能夠通過深入淺出的方式,讓大家對MVC有清楚的認識。
1.MVC框架模式的工作原理
傳統的基于PHP語言的Web應用程序,把PHP代碼和HTML、CSS、JavaScript代碼混合在一起,不利于代碼的后期維護,同時也不利于程序功能的擴展。基于MVC的應用程序,把應用程序中的各個功能獨立出來,可以很好地實現程序功能的分工合作,對于代碼的維護和擴展十分方便。
MVC是一種目前廣泛流行的框架模式。近來,隨著PHP的成熟,它正在成為在LAMP平臺上推薦的一種框架設計模式,也是廣大PHP開發者非常感興趣的框架設計模式,并有增長的趨勢。隨著網絡應用的快速增加,MVC模式對于Web應用的開發無疑是一種非常先進的設計思想,無論你選擇哪種語言,無論應用多復雜,都能為你理解分析應用模型提供最基本的分析方法,為你構造產品提供清晰的設計框架,為你的軟件工程提供規范的依據。MVC的設計思想是把一個應用的輸入、處理、輸出流程按照模型(Model)、視圖(View)、控制器(Controller)的方式進行分離,這樣的一個應用被分成三個層--模型層、視圖層、控制層),下面分別進行介紹。
(1).視圖(View)
視圖是用戶看到的并與之交互的界面。視圖可以向用戶顯示相關的數據,并能接收用戶的輸入數據,但它并不進行任何實際的業務處理。視圖可以向模型查詢業務狀態,但不能改變模型。視圖還能接受模型發出的數據更新事件,從而對用戶界面進行同步更新。作為視圖來講,它只是作為一種輸出數據并允許用戶操作的方式。
(2).模型(Model)
MVC的三個部件中,模型是主體部分,包含業務數據和業務邏輯,同時負責訪問和更新持久化數據,一個模型能為多個視圖提供數據,每個視圖都從不同角度來表達模型。由于應用于模型的代碼只需寫一次就可以被多個視圖重用,所以減少了代碼的重復性。
(3).控制器(Controller)
控制器負責協調整個應用程序的運轉,控制器的作用就是接收瀏覽器端的請求。它接收用戶的輸入并調用模型和視圖去完成用戶的需求,當用戶單擊Web頁面中的超鏈接或發送HTML表單時,控制器本身不輸出,只是接收請求并決定調用哪個模型去處理瀏覽器端發出的請求,然后確定用哪個視圖來顯示模型處理返回的數據。
MVC處理過程如圖1-3所示,首先控制器接收用戶的請求,并決定應該調用哪個模型來處理;然后模型根據用戶請求進行相應的業務邏輯處理,并返回數據;最后控制器調用相應的視圖來格式化模型返回的數據,并通過視圖呈現給用戶。
圖 1?3? MVC設計模式
2. MVC模式的優點
使用PHP開發出來的Web應用,初始的開發模板就是混合的數據編程。例如,直接向數據庫發送請求并用HTML顯示,開發速度往往比較快,但由于數據頁面的分離不是很直接,因此很難體現出業務模型的樣子或者模型的重用性。產品設計彈性力度很小,很難滿足用戶的多樣化的需求。MVC要求對應用分層,雖然要花費額外的工作,但產品的結構清晰,產品的應用通過模型可以得到更好的體現。
首先,最重要的是應該有多個視圖對應一個模型的能力。在目前用戶需求的快速變化下,可能有多種方式訪問應用的要求。例如,訂單模型可能有本系統的訂單,也有網上訂單,或者其他系統的訂單,但對于訂單的處理都是一樣,也就是說訂單的處理是一致的。按 照MVC 設計模式,一個訂單模型以及多個視圖即可解決問題。這樣減少了代碼的復制,即減少了代碼的維護量,一旦模型發生改變,也易于維護。其次,由于模型返回的數據不帶任何顯示格式,因而這些模型也可直接應用于接口的使用。再次,由于一個應用被分離為三層,因此有時改變其中的一層就能滿足應用的改變。一個應用的業務流程或者業務規則的改變只需改動MVC的模型層。
控制器還有一個好處,就是可以用它來連接不同的模型和視圖去完成用戶的需求,這樣它可以為構造應用程序提供強有力的手段。給定一些可重用的模型和視圖,控制器可以根據用戶的需求選擇模型進行處理,然后通過視圖將處理結果顯示給用戶。
最后,MVC還有利于軟件工程化管理。由于不同的層各司其職,每一層不同的應用具有某些相同的特征,有利于通過工程化、工具化產生管理程序代碼。
綜上所述,MVC是構筑軟件非常好的框架模式,將業務處理與顯示分離,強制地將應用分為模型、視圖及控制層。總之,MVC模式會使得應用更加強壯,更加有彈性,更加個性化。
3. MVC框架模式的實現
在實現MVC框架模式之前,我們先來回顧一下不使用MVC的開發流程。這里有一個網站的三個頁面,分別是首頁(index.html),列表頁(arc_list.html)和內容頁(article.html)。這三個頁面都是靜態頁面。接下來實現靜態頁面改寫成PHP動態頁面,以便能及時從數據庫中讀取最新內容。這里只實現首頁中的“行業百科”模塊,效果如圖1-4所示。
圖1-4 ?首頁中“行業百科”模塊效果圖
靜態頁面index.html中“行業百科”模塊代碼如下。
<div class="title2 indextt4"><span><a title=行業百科 href="#">行業百科</a></span><em><a href="#">更多 >> </a></em> </div> <div class="rightList2 marginbtm15"><ul class=ulRightList1s> <?php$dbh = new PDO('mysql:dbname=dscms;host=127.0.0.1','root','aa09090909');$dbh->exec("set names 'utf8'");$query = "SELECT title FROM ds_article WHERE cid='14'";try {//執行SELECT查詢,并返回PDOstatement對象 $pdostatement = $dbh->query($query);$result=$pdostatement->fetchAll();foreach ($result as $row) { ?><li><A title=<?php echo $row["title"]; ?> href="#" target=_blank><?php echo $row["title"];?></A></li> <?php }} catch (PDOException $e) {echo $e->getMessage();} ?></ul> </div>
實現了首頁中“行業百科”從數據庫查詢功能后,首頁中其他功能,還有列表頁,內容頁的實現過程也和 “行業百科”類似,這里就不列舉了。
如此編寫代碼是很多初學者經歷的一個階段,就是將PHP代碼和HTML、CSS、JavaScript代碼混合在一起使用,如果有人之前這樣去做的話,能否體會出代碼混合在一起編寫所帶來的麻煩?
首先就是不利于代碼的重復使用,如剛才的“行業百科”模塊。如果在列表頁和內容頁中也有一樣的模塊,則需要重復編寫,或者把這部分的代碼放到一個文件中,頻繁使用include語句去調用。或者代碼連重復利用都不行,如數據庫操作等。其次就是不利于較大項目的團隊合作,如開發人員不需要使用HTML、CSS和JavaScript等技術;前臺美工不需要使用數據庫、PHP開發。最后,不利于代碼的后期擴展。如在后期的項目維護過程中,代碼混雜,層次不清,將導致重復修改。
如何解決這些不利于軟件開發的缺點呢?毫無疑問,MVC框架模式就能解決,具體處理流程如下。步驟1:創建models/Article.php,并在文件中定義文章表模型類Article,其中的find()方法返回查詢數據的結果。
<?php class CController{/**加載指定目錄下的模板文件,并將控制器中的數據傳遞到視圖文件中@param string $fileName 提供模板文件的文件名@param array 變量名=>變量值*/public function render($viewName, $data){ extract($data, EXTR_PREFIX_SAME,'data');//將數組$data變成變量的形式require($viewName);//包含視圖文件} } ?> 步驟3:創建Controllers/DefaultController.php文件,創建控制器DefaultController繼承父類CController,創建首頁管理方法actionIndex(),在其中創建模型Article對象,并調用find()方法獲取數據,渲染視圖,并把數據輸出到視圖頁面。
<?php require '../framework/CController.php';//導入框架文件 require '../models/Article.php';//導入文章表模型類文件 class DefaultController extends CController {//首頁管理public function actionIndex(){//創建模型對象$article=new Article();//獲得數據$result=$article->find();//渲染視圖,并把數據輸出到視圖頁面$this->render("../views/index.php",array("result"=>$result));}//列表頁管理public function actionList(){}//內容頁管理public function actionArticle(){} } $default_con = new DefaultController(); $default_con->actionIndex(); ?> 步驟4:創建views/index.php,在視圖文件中,對查詢結果變量$resule進行循環處理,生成完整的HTML頁面。
<DIV class="rightList2 marginbtm15"> <UL class=ulRightList1s> <?phpforeach ($result as $row) { ?><li><A title=<?php echo $row["title"]; ?> href="#" target=_blank><?php echo $row["title"];?></A></li> <?php } ?> </UL> </DIV> 實現的 MVC 框架執行流程如圖 1-5 所示。
1. 用戶直接調用控制器實例對象。控制器調用類中的 action方法(動作)。
2. 控制器調用模型實例對象從數據庫中讀取數據。
3. 渲染視圖。
4. 視圖讀取并顯示模型的屬性。
5. 動作完成視圖渲染并將其返回給用戶。
本節中按照MVC框架模式的工作思想,完成了控制器、模型、視圖三部分的代碼分離。我們訪問程序,需要去訪問controllers目錄下的控制器文件,這樣做存在明顯的設計缺陷,如果控制器文件較多,則會導致系統結構訪問混亂,后期維護困難、安全性差等一系列問題,而且不便于系統的統一管理。
下一節中將新增入口文件,通過解析用戶請求的URL,提取出控制器名和動作方法名,創建相應控制器實例對象,并執行動作方法。
總結