轉老逆原創帖!
VC模式下,客戶直接發送請求到控制器,控制器根據用戶請求的資源分發到相對應的模型來處理,模型完成了業務邏輯後,把所要的數據發送到視圖,視圖顯示返回給客戶。這就是web 或是說B/S架構的MVC工作流程。
控制器:
用戶的所有請求會發送到控制器,由控制器來根據需要調用模型和視圖。比如用戶請求index.php 控制器文件,index.php裏面不會設計到任何的數據庫操作、邏輯操作。它只會尋找執行用戶請求的業務模型,把所有的業務邏輯操作交給模型也就是MVC中的M。把控制器獨立出來,形成單入口訪問模式,方便做全局管理,比如:日誌記錄等。
模型:
模型是業務邏輯數據的集合,比如數據庫操作,複雜的邏輯運算等。按照功能或項目模塊來分成一個個模型,模型間的耦合性很小有利於項目以後的擴展和修改。
視圖:
Web技術中的MVC的C層。其主要是由 HTML 、XML語言組成的界面。以前的web界面是視圖和模型混雜在一起使用,形成了雜亂的代碼,這樣使日後程序的維護十分艱難。PHP中知名的模板引擎smarty 就是爲了實現模型和視圖分離的一種技術。現在smarty 在PHP行業中被開發者廣泛使用。
MVC思想不是爲了某種語言而設計的,它適用於所有的面向對象的語言。比如知名的實現MVC思想的JAVA語言的 Struts 框架。當然PHP 框架也是百花齊放如 :Zend Framework 、 Fleaphp 、Thinkphp 、Cakephp 等,都能很好的實現MVC思想而且他們大量應用了GOF 設計模式,開發人員如果基於以上幾種MVC框架來進行項目開發的話,開發的效率和代碼質量都會大幅度提升,特別是多人協作開發的項目。那PHP怎麼實現MVC的呢?下面給大家開發一個簡單的MVC基礎框架來說明這一點,完整代碼附光盤09/20。
類驅動
在php5中可以使用__autoload 函數來實現類自動加載。但單純這樣的方式不夠靈活的。比如類文件存放在不同的目錄裏面,而此時又需要自動加載的情況下,我們就需要在__autoload函數裏進行復雜的邏輯判斷來實現自動加載。
比如需要實例化兩個類:Myblog 、Mybook。Mylog類在根目錄下的Lib/test.php 文件裏,Mybook類在根目錄下的App/command.php 文件裏。
__autoload
函數裏實現加載:
[php]<?php
function __autoload($class){
if($class =='Myblog') include 'Lib/test.php';
if($class =='Mybook') include 'App/command.php';
if(!include_once($classpath)){//加栽類
throw newException("加載類庫失敗");
}
}
[size=10.5pt]?>
這只是實例化兩個不同目錄下的兩個類而已。如果項目中使用面向對象開發的話,類不會那麼少,大家可以想像一下。如果要加載數個不同目錄下的類,在__autoload函數裏實現會是多麼的麻煩和不靈活。
在這裏給出個比較簡單的解決方案,而且這個解決方案在很多MVC框架中都得以很好的應用
我們只需要在類的命名方式上做些改變,以類的目錄路徑爲類名:
/Yhmphp/ App.php 裏的Mysession類,命名爲:Yhmphp_App_Mysession。用‘ _ ’下劃線來替換
路徑分割符,以相對路徑下的目錄路徑做類名。
又比如根目錄下的Lib目錄下test.php 文件裏面(/Lib/test.php) 有個Myblog 類,可以這樣給Myblog類命名:
<?php
class Lib_Myblog{}
?>
實例化Lib_Myblog類:
<?php
$myblog = newLib_Myblog;
[size=10.5pt]?>
__autoload函數裏用str_replace函數把路徑分割符替換類名中的‘ _ ’下劃線,這樣就可以準確的找到Lib_Myblog類的所在文件的路徑然後準確的加載了:
<?php
function __autoload($class){
$classpath =str_replace('_','/',$class).'.php';
if(!include_once($classpath)){//加栽類
throw newException("加載類庫失敗");
}
}
[size=10.5pt]?>
模型的路由:
當我們給單入口文件(控制器)index.php
加上了模型選參m(index.php?m=myblog),控制器就會去尋找 myblog模型類,並實例化,然後執行myblog模型類中的 model 方法,最後執行show方法
來顯示視圖。
<?php
class App_Run
{
publicfunction routing(){
$model =MOBILE_MODEL.'_'.MODEL_SWITCHING.'_'.$_REQUEST['m'];
if(class_exists($model)){
$cake= new $model;
method_exists($cake,'model')&& $cake->model(); //執行模型裏面的 model方法
method_exists($cake,’show’)&& $cake->show(); //視圖層
}else{
thrownew Exception("數據模型不存在");
}
}
}
[size=10.5pt]?>
常量MBILE_MODEL定義了模型存放的目錄名,在這裏做了定義方便日後模型目錄的更改。這是個良好的習慣。能統一定義的信息就該統一。能模塊化的業務邏輯就應該模塊化。爲日後的項目維護和項目擴展做好鋪墊。
MBILE_MODEL
在Config/__Active.php文件裏面這樣定義:
[php]<?php
/**
系統配置
*/
define('MOBILE_ROOT', ''); //站根目錄
define('MOBILE_MODEL','Modules'); //模型目錄名
[size=10.5pt]?>
class_exists() 函數來判斷客戶請求的($_REQUEST['m']) 模型類是否存在。Class_exists() 方法依據__autoload() 函數來加載判斷。所以__autoload()函數必須在class_exists() 方法之前先加載。
模型類如果存在,就使用method_exists() 方法來判斷執行模型類裏面的model 方法裏面的業務邏輯,然後再執行show() 方法顯示視圖。這樣就完成了一個 MVC 流程。
存放模型的目錄是 Modules 目錄。這個在上面的MOBILE_MODEL常量中已經定義。難道所有的業務模型都存在一個Modules目錄裏面嗎?這樣的設計的確有點問題。文件夾裏面的文件過多,損耗程序尋找模型加載的時間,而且模型過多存在一個目錄之中,會讓這個項目變得很雜亂。比如前臺和後臺的模型目錄和視圖目錄就應該分開存放。
<?php
/**
多模型目錄
*/
if(empty($_GET['c']) && ) $_GET['c'] ='Default';
if(empty($_GET['m'])) $_GET['m'] = 'Index';
define ('MODEL_SWITCHING',$_GET['c']) ; //前臺後臺目錄切換
[size=10.5pt]?>
常量MODEL_SWITCHING 就是爲解決這個問題設計的:
我們先看最後一句代碼:
[size=10.5pt]define('MODEL_SWITCHING',$_GET['c']) ; //前臺後臺目錄切換
它定義了常量MODEL_SWITCHING 以$_GET ['c']變量爲值。App_Run 類中,常量MOBILE_MODEL 、MODEL_SWITCHING 和$_GET['m'] 組成了模型的加載路徑:
$model = MOBILE_MODEL.'_'.MODEL_SWITCHING.'_'.$_GET['m'];
這樣設計以後我們想添加多個模型目錄都是很容易的事情了。比如:項目需要添加兩個模型目錄。前臺模型目錄:Default 和 後臺模型目錄:Admin 。只需要在默認的模型目錄 Modules 下創建 Default 和Admin 兩個目錄,然後客戶訪問的URL 中 添加一個 參數c :
訪問Default 目錄下的Myblog業務模型: index.php?c=default&m=myblog
訪問Admin 目錄下的Member業務模型: index.php?c=admin&m=member
我們可以再設計靈活及人性化點,就是當$_GET[‘m’]和$_GET[‘c’] 客戶沒有設置的時候來給模型目錄和業務模型類設置一個默認值。
if(empty($_GET['c'])
) $_GET['c'] = 'Default';
if(empty($_GET['m'])) $_GET['m'] = 'Index';
控制器
PHP MVC典型的設計就是使用單入口php文件來實現MVC中的C:控制器。所有的客戶請求全部經過index.php 控制器集中控制。然後按需進行分發:
<?php
/**
入口文件
*/
try{
error_reporting(E_ALL);//關閉錯誤輸出
require'Config/__Homeswitching.php';
require'Config/__Active.php';
require'App/Auto.php';
$set = newApp_Run;
$set->routing();
}catch (Exception $e){
echo($e->getMessage());
}
[size=10.5pt]?>
多模型設置文件:__Homeswitching.php,定義基礎模型目錄文件:__Active.php,類驅動文件:Auto.php
加載完後,再實例化模型路由App_Run類,執行其 routing() 方法 尋找完成客戶請求。
業務模型
業務模型類是整個項目中使用最多的。它就是MVC 中的M (模型)。類裏面封裝了大量的業務邏輯。比如:客戶向index.php 控制器 請求 (index.php?m=newbook)newbook業務模型,來查詢最新出的圖書。 那麼newbook業務模型類所需要完成的任務就是查詢數據庫,從數據庫中提取最新的圖書資料,然後發送到視圖層(View)顯示給客戶。顯然,MVC 模式可以把 模型與模型、功能與功能之間的耦合度變得很小、擴展性很強。
<?php
/**
* 模型類
* */
class Modules_Default_Index
extends App_Manage
{
privatenewbook=’’;
publicfunction __c****truct(){
parent::__c****truct();
}
publicfunction show(){
$this->tpl->assign(‘newbook’,$this->newbook);
$this->tpl->display('Index');
}
publicfunction model(){
/**實現業務邏輯*/
$this->newbook = ‘PHP MVC’;
}
}
[size=10.5pt]?>
業務模型類裏面有兩個主要方法: model() 、show() 方法。Model() 方法主要實現業務邏輯,比如:讀數據庫、寫數據庫等。Show() 方法主要和model() 方法聯繫獲取業務邏輯的數據,然後輸出給視圖(php模板)。
讀者請思考下以下幾個問題:
模型類 Modules_Default_Index 爲什麼要起這個名字?前面__autoload講解中已講解。Show() 方法是否能去掉只實現model() 模型?模型的路由小節中講解。
全局類
在項目開發裏,有很多我們自己封裝好的類:數據庫操作類、模板類、email發送類、分頁類、文本緩存類、內存緩存類memcache等等。這些類中有些是每個模型都必須加載使用的,比如 數據庫操作類、分頁類。難道我們每個模型裏都要顯式的實例化一次?那真是太麻煩了。所以設計一個全局管理類App_Manage :
<?php
class App_Manage{
protected$tpl;
protected$mem;
protected$email;
publicfunction __c****truct(){
$this->tpl = new Lib_Tpl;
/*
$this->mem = new Lib_Memcached;
$this->email= new Lib_Mailer();*/
}
[font=宋體]}[/font]
[font=宋體][size=10.5pt]?>
在App_Manage中。我們簡單的在其構造方法中實例化了幾個常用的類。每個模型都繼承App_Manage類,這樣模型類裏面就可以使用父類的方法和屬性,以此實現全局類:
public function __c****truct(){
parent::__c****truct();
[size=10.5pt]}[/size]
<SPAN
style="COLOR: #007700">
本篇文章來源於PHP論壇原文鏈接:http://bbs.php.cn/thread-20644-1-1.html