在PHP中應用MVC的介紹(二)

來源:互聯網
上載者:User

(Walter.Fan編譯自Jason E. Sweat寫的An Introduction to MVC Using PHP)

下面來看一個簡單的MVC應用程式執行個體:
說得天花亂墜,不如實際動和做個簡單的例子.在這個例子中,我們以Phrame架構來
實現MVC模式. Phrame是Jakarta Struts的一個PHP實現方案,它的控制檔案不象struts
那樣是XML檔案,而是PHP中最常用的數組.

在Phrame中,和Struts一樣,通過Action,  Forms and Forwards這幾個類
將模型和視圖鬆散耦合在一起.
 每一次controller初始化,它都會建立一個擴充自Action類的子類的執行個體
controller會調用你的Action子類的Process()方法,
一個Forwards的列表和一個包含所有HTTP request 變數的Form對象

你的Action的期望結果是去確定適當的Forward對象返回給controller.
controller會處理這個Forward對象並退出對於這次請求的處理過程

Action對象的作用,可以以它自己在HTTP request 和一個句子之間做一個類比
你可以認為在這個句子中,Action是一個動詞, Model是一個名詞, View是一個形容詞.
換句話說,一個特定的Web請求會在一個Model(名詞)上執行一個Action(動詞),
或者顯示一個View來描述(形容詞)一個Model(名詞)

這個類比描述了Action和Model關係,在選擇對象名時可以加以考慮

這個例子是Phrame的Hello World例子的一個修改版本 (http://phrame.itsd.ttu.edu/).
原始碼可由http://sourceforge.net/projects/phrame下載
在這個例子中,我去掉了PHP的一些警告 ,用Smarty代替XSLT來格式化視圖

MVC應用程式使用一個引導(bootstap)檔案,它是一個單獨的PHP檔案,是應用程式的核心
本例中,這個bootstap檔案是hello.php,讓我們來看看這個bootstap檔案
<?
error_reporting(E_ALL);
define('PHRAME_LIB_PATH', '../../phrame/');
require_once PHRAME_LIB_PATH.'include.jes.php';
require_once 'Smarty.class.php';
require_once 'MappingManager.php';
require_once 'actions/HelloAction.php';
require_once 'models/Person.php';
require_once 'models/HelloErrors.php';
define('APPL_VIEW', 'hello.php?'._VIEW.'=');
define('APPL_ACTN', 'hello.php');
?>
在一個Phrame程式中,Forms, Actions and Forwards的關係是
在傳統的Phrame選項數組中建立的.我覺得這個數組單調冗長不利於開發和維護
就建立了一個MappingManager類來管理維護這些關係
這個類可以被任一個Phrame應用程式擴充,它有方法可返回
Phrame使用的PHP數組.
這個類的好處是在你增加它們進可以驗證Form和action mapping之間的關係
(避免了在你嘗試使用Phrame選項數組時產生錯誤)
class HelloMap extends MappingManager
{
function HelloMap()
{
//set options
$this->_SetOptions('handle_error');
//add application forms
$this->_AddForm('helloForm','ActionForm');
//add application actions and forwards
$this->_AddMapping('sayHello','HelloAction', APPL_VIEW.'index','helloForm');
$this->_AddForward('sayHello', 'index');
$this->_AddForward('sayHello', 'hello',APPL_VIEW.'hello');
}
}

在 HelloMap class的建構函式中完成了我們所要做的.
首先,重載傳統的Phrame選項數組,我們定義了一個錯誤處理函數
接著,看需要識別什麼Form,在這個例子中,不需要擴充標準的Phrame ActionForm類
最後,定義actions和forwards, 本例中,只有一個action: sayHello
兩個Forwards:“index” 和 “hello”
MappingManager::_AddMapping() 方法的參數是
(mapping的名字, 實現這個mapping的類, 調用action的預設地址,mapping中與action關聯的form)

 MappingManager::_AddForward()方法的參數是
action mapping 的標識符,forward的標識符和可選的重新導向URL(如果沒有action與之關聯)

<?
session_start();
$smarty =& new Smarty;
$map =& new HelloMap;
$controller =& new ActionController($map->GetOptions());
$errors =& new HelloErrors;
function handle_error($number, $message, $file,$line, $context)
{
 appl_error($message);
}
function appl_error($psErrorMsg)
{
 $errors =& new HelloErrors;
 $errors->AddError($psErrorMsg);
}

?>

接下來,我們start the session, 建立幾個我們要用到的對象
Smarty template object
HelloMap class object(我們先前定義的)
Phrame ActionController Object
(它需要一個選項數組,可由MappingManager::GetOptions()獲得)

這節的代碼還加入了一個錯誤Model,封裝了錯誤處理實際上的實現方案
(儲存錯誤在$_SESSION的一個資料中, 參見HelloErrors.php)

並定義了兩個函數
appl_error(),允許你增加一個錯誤到系統的錯誤對象中去
handle_error() 一個PHP錯誤處理函數,它調用了函數appl_error()
這就是說,你可以直接用函數appl_error(),或者用
trigger_error()(如果PHP的錯誤處理控制代碼設成handle_error())

<?

if (array_key_exists(_ACTION,$_REQUEST))
{
 //release control to controller for
 //further processing
 $controller->Process($map->GetMappings(),
 $_REQUEST);
}
else
{
 //determine and display view
 $requested_view = (array_key_exists(_VIEW,
 $_REQUEST)) ?
 strtolower($_GET[_VIEW]) :
 'index';
 switch ($requested_view) {
 case 'hello':
 $template = $requested_view.'.tpl';
 //assign view specific data
 $person =& new Person;
 $smarty->Assign('name',
 $person->GetName());
 break;
 case 'index':
 default:
 $template = 'index.tpl';
}
//assign common data
$smarty->Assign(array(
'appl_link' => APPL_ACTN
,'appl_view' => APPL_VIEW
,'action' => _ACTION
));
//assign and clear errors
$smarty->Assign('errors',
$errors->GetErrors());
$smarty->Display($template);
exit;
}
餘下的代碼實現了MVC的controller組件
if語句判斷是否請求了一個action.
如果是的,調用Phrame
ActionController::Process()方法啟用這個架構.
如果沒有指定的請求,顯示一個View

else語句確定合適的View,
assign特定的資料到smarty模板
assign通用的資料到smarty模板
處理任何可能發生的錯誤
然後render渲染模板

跳出這個程式,我們來看看到底我們的應用程式中加入什麼東西來使用Phrame
首先, 要考慮如果根本沒有參數傳入,我們的應用程式該幹什麼.
在本例中,跳過action處理,渲染index.tpl模板以顯示預設的視圖
這兒是Smarty template index.tpl的一部分

<form action="{$appl_link}" method="post">
<div>
<input type="hidden" name="{$action}" value="sayHello" />
{if $errors|@count gt 0}
<ul>
{section name=e loop=$errors}
<li><b style="color:
red">{$errors[e]}</b></li>
{/section}
</ul>
{/if}
What is your name?<br />
<input type="text" name="name" value="{$name}"
/>
<input type="submit" value="OK" />
</div>
</form>

重要的是要檢查什麼進行中中.
這個HTML表單提交給應用程式指令碼本身,它有一個隱藏變數,
以模板變數$action命名為“sayHello”,
這就是指引controller去初始化 “sayHello” mapping的輸入.

模板的下面一節是錯誤條件檢查,如果有錯誤,就顯示一個無序列表.
模板的最後一部分是通常表單的顯示部分,一個users name文本輸入框和一個提交按鈕

那麼,當使用者點擊提交按鈕後會怎樣呢?
瀏覽器會提交一個HTTP POST給這個bootstrap指令碼.
$_REQUEST[_ACTION]值為“sayHello”. 作為響應,
$controller 會執行一個Perform()方法
回顧我們的HelloMap類中的mapping, “sayHello”和HelloAction類關聯
ActionController::Perform()方法會建立 HelloAction類的一個執行個體並
執行HelloAction::Perform()方法, 見下面的代碼

class HelloAction extends Action
{
 function &Perform(&$poActionMapping,&$poActionForm)
 {
  $person =& new Person();
  $errors =& new HelloErrors;
  $name = $poActionForm->Get('name');
  //get ActionForward depending on if
  //errors were generated
  if ((!$person->SetName($name)) ||($errors->HasErrors()))
  {
   $actionForward =$poActionMapping->Get('index');
  }
  else
  {
   $actionForward =$poActionMapping->Get('hello');
  }
  return $actionForward;
 }
}

在你的應用程式中所需的每一個action都類似於這個檔案
你可建立一個Phrame Action的子類,並重載 Perform()方法去實現一個action
最後,你的程式需要返回一個ActionForward 對象給controller去完成處理.

讓我們察看一下HelloAction 代碼的細節
最開始,因為效能原因,以傳址方式將兩個指定對象傳給函數Perform
這個函數將以傳址方式返回ActionForward對象
然後,建立模型的兩個執行個體$person and $errors.
$poActionForm是由controller根據你的mappings建立的Phrame ActionForm類的執行個體
我們指定我們的Form為 ActionForm,而不是繼承它,因為沒有必需的理由
ActionForm類本身繼承了Phrame 的工具類 HashMap 類,並隨著$_REQUEST 關聯資料預載
所以如果POST提交過後,$poActionForm->Get()方法會得到posted的變數
在本例中,我們想求使用者提交的文本輸入框中輸入的name
接著,根據medel中的Person::SetName()方法是否成功,
由我們的action確定返回哪一個ActionForward mapping
同時,它也驗證這個actionr結果沒有錯誤觸發. 如果有問題,
它會返回到“index” view,否則,顯然去顯示“hello” view.

最後我們來看看Person.php這個檔案,對每個應用程式來說,
Models都是獨一無二的,因而Phrame庫中不可能有其原型,
也不必從Phrame類中繼承任何類. 這個Model使用了兩個常量
,把它們作為model類名的首碼以避免重名.

回顧我們的模型的內部細節,我們發現名稱儲存在PHP的session中.
重點注意它在我們的類以外,這個儲存的細節是未知的, 你可在任何時候
改變類的定義去改變這個實現方法.
由於在我們的應用程式中所有對於session資料索引的操作都是通過單一的model類來完成的
你可始建立正確的錯誤處理,商務邏輯(類似於確認規則)
並加強了你的應用程式的資料一致性.
這就是MVC的貢獻,便利我們可能更好的維護在持久性存貯中的資料

在這個模型中另一個令人感興趣的就是SetName() 方法,
特別的,我們的商務邏輯規定一個名字必須小於20個字元,
如果以一個超出範圍的名字長度值調用這個模型,就會觸發一個錯誤

未來的方向
怎麼擴充基於Phrame的MVC應用程式呢?
如果你需要一個額外的資料檢視,你可以簡單地增加一個新的模板
並擴充"case"語句以在bootstrap檔案中確定一個有效視圖.

要增加一個新的action,你需要建立一個新的繼承於Action的類
就象我們寫的HelloAction類, 你還需要在HelloMap 類的建構函式中
增加一個 mapping以便讓MVC知道當請求新的action時,該去初始化哪一個類
最後,當action完成時你需要增加forwards指向合適的View

我們開發的大多數model類都會用到資料庫,
很多時候,對於Phrame我們有兩個基本的方面的改變.
首先,實現一個預設的action去顯示一個view
其次,在本例中並未完全貫徹物件導向風格,可以以Factory模式
在預設的“ShowView” action中建立應用程式view的一個執行個體

這個例子的另一個問題是包含檔案有點低效:
每次請求都需要包含action類,甚至顯示一個特殊請求的結果的view.
你可以重新設定你的應用程式,對上下文更加敏感,只包含必需的資源.

回顧一下,我們看到了以MVC架構實現PHP的好處.
並察看了一個用session作為持久性資料存貯的model,
怎樣使用HelloMap和Phrame和 ActionController來控制應用程式的流程,
在應用程式中怎樣使用Smarty templates來實現View

這個例子非常簡單,但它示範了處理使用者輸入,資料校正,錯誤處理和應用程式流程程
通過學習這個例子,我們擁有了開發基於MVC的web程式的基礎

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.