如何寫mvc基礎框架

 轉老逆原創帖!

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

發佈了41 篇原創文章 · 獲贊 0 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章