風(fēng)人園

          弱水三千,只取一瓢,便能解渴;佛法無(wú)邊,奉行一法,便能得益。
          隨筆 - 99, 文章 - 181, 評(píng)論 - 56, 引用 - 0
          數(shù)據(jù)加載中……

          MVC模式的PHP實(shí)現(xiàn)(zt)

          MVC模式在網(wǎng)站架構(gòu)中十分常見(jiàn)。它允許我們建立一個(gè)三層結(jié)構(gòu)的應(yīng)用程式,從代碼中分離出有用的層,幫助設(shè)計(jì)師和開(kāi)發(fā)者協(xié)同工作以及提高我們維護(hù)和擴(kuò)展既有程式的能力。

          視圖(View)

          “視圖”主要指我們送到Web瀏覽器的最終結(jié)果??比如我們的腳本生成的HTML。當(dāng)說(shuō)到視圖時(shí),很多人想到的是模版,但是把模板方案叫做視圖的正確性是值得懷疑的。

          對(duì)視圖來(lái)說(shuō),最重要的事情可能是它應(yīng)該是“自我意識(shí)(self aware)”的,視圖被渲染(render)時(shí),視圖的元素能意識(shí)到自己在更大框架中的角色。

          以XML為例,可以說(shuō)XML在被解析時(shí),DOM API有著這樣的認(rèn)知??一個(gè)DOM樹(shù)里的節(jié)點(diǎn)知道它在哪里和它包含了什么。 (當(dāng)一個(gè)XML文檔中的節(jié)點(diǎn)用SAX解析時(shí)只有當(dāng)解析到該節(jié)點(diǎn)時(shí)它才有意義。)

          絕大多數(shù)模板方案使用簡(jiǎn)單的過(guò)程語(yǔ)言和這樣的模板標(biāo)簽:

          <p>{some_text}</p>
          <p>{some_more_text}</p>


          它們?cè)谖臋n中沒(méi)有意義,它們代表的意義只是PHP將用其他的東西來(lái)替換它。

          如果你同意這種對(duì)視圖的松散描述,你也就會(huì)同意絕大多數(shù)模板方案并沒(méi)有有效的分離視圖和模型。模板標(biāo)簽將被替換成什么存放在模型中。

          在你實(shí)現(xiàn)視圖時(shí)問(wèn)自己幾個(gè)問(wèn)題:“全體視圖的替換容易嗎?”“實(shí)現(xiàn)一個(gè)新視圖要多久?” “能很容易的替換視圖的描述語(yǔ)言嗎?(比如在同一個(gè)視圖中用SOAP文檔替換HTML文檔)”


          模型(Model)

          模型代表了程序邏輯。(在企業(yè)級(jí)程序中經(jīng)常稱(chēng)為業(yè)務(wù)層(business layer))

          總的來(lái)說(shuō),模型的任務(wù)是把原有數(shù)據(jù)轉(zhuǎn)換成包含某些意義的數(shù)據(jù),這些數(shù)據(jù)將被視圖所顯示。通常,模型將封裝數(shù)據(jù)查詢,可能通過(guò)一些抽象數(shù)據(jù)類(lèi)(數(shù)據(jù)訪問(wèn)層)來(lái)實(shí)現(xiàn)查詢。舉例說(shuō),你希望計(jì)算英國(guó)年度降雨量(只是為了給你自己找個(gè)好點(diǎn)的度假地),模型將接收十年中每天的降雨量,計(jì)算出平均值,再傳遞給視圖。


          控制器(controller)

          簡(jiǎn)單的說(shuō)控制器是Web應(yīng)用中進(jìn)入的HTTP請(qǐng)求最先調(diào)用的一部分。它檢查收到的請(qǐng)求,比如一些GET變量,做出合適的反饋。在寫(xiě)出你的第一個(gè)控制器之前,你很難開(kāi)始編寫(xiě)其他的PHP代碼。最常見(jiàn)的用法是index.php中像switch語(yǔ)句的結(jié)構(gòu):

          <?php
          switch ($_GET['viewpage']) {
          case "news":
          $page=new NewsRenderer;
          break;
          case "links":
          $page=new LinksRenderer;
          break;
          default:
          $page=new HomePageRenderer;
          break;
          }
          $page->display();
          ?>


          這段代碼混用了面向過(guò)程和對(duì)象的代碼,但是對(duì)于小的站點(diǎn)來(lái)說(shuō),這通常是最好的選擇。雖然上邊的代碼還可以優(yōu)化。

          控制器實(shí)際上是用來(lái)觸發(fā)模型的數(shù)據(jù)和視圖元素之間的綁定的控件。


          例子

          這里是一個(gè)使用MVC模式的簡(jiǎn)單例子。
          首先我們需要一個(gè)數(shù)據(jù)庫(kù)訪問(wèn)類(lèi),它是一個(gè)普通類(lèi)。

          <?php
          /**
          *  A simple class for querying MySQL
          */
          class DataAccess {
          /**
          * Private
          * $db stores a database resource
          */
          var $db;
          /**
          * Private
          * $query stores a query resource
          */
          var $query; // Query resource
          //! A constructor.
          /**
          * Constucts a new DataAccess object
          * @param $host string hostname for dbserver
          * @param $user string dbserver user
          * @param $pass string dbserver user password
          * @param $db string database name
          */
          function DataAccess ($host,$user,$pass,$db) {
          $this->db=mysql_pconnect($host,$user,$pass);
          mysql_select_db($db,$this->db);
          }
          //! An accessor
          /**
          * Fetches a query resources and stores it in a local member
          * @param $sql string the database query to run
          * @return void
          */
          function fetch($sql) {
          $this->query=mysql_unbuffered_query($sql,$this->db); // Perform query here
          }
          //! An accessor
          /**
          * Returns an associative array of a query row
          * @return mixed
          */
          function getRow () {
          if ( $row=mysql_fetch_array($this->query,MYSQL_ASSOC) )
          return $row;
          else
          return false;
          }
          }
          ?>


          在它上邊放上模型。

          <?php
          /**
          *  Fetches "products" from the database
          */
          class ProductModel {
          /**
          * Private
          * $dao an instance of the DataAccess class
          */
          var $dao;
          //! A constructor.
          /**
          * Constucts a new ProductModel object
          * @param $dbobject an instance of the DataAccess class
          */
          function ProductModel (&$dao) {
          $this->dao=& $dao;
          }
          //! A manipulator
          /**
          * Tells the $dboject to store this query as a resource
          * @param $start the row to start from
          * @param $rows the number of rows to fetch
          * @return void
          */
          function listProducts($start=1,$rows=50) {
          $this->dao->fetch("SELECT * FROM products LIMIT ".$start.", ".$rows);
          }
          //! A manipulator
          /**
          * Tells the $dboject to store this query as a resource
          * @param $id a primary key for a row
          * @return void
          */
          function listProduct($id) {
          $this->dao->fetch("SELECT * FROM products WHERE PRODUCTID='".$id."'");
          }
          //! A manipulator
          /**
          * Fetches a product as an associative array from the $dbobject
          * @return mixed
          */
          function getProduct() {
          if ( $product=$this->dao->getRow() )
          return $product;
          else
          return false;
          }
          }
          ?>


          有一點(diǎn)要注意的是,在模型和數(shù)據(jù)訪問(wèn)類(lèi)之間,它們的交互從不會(huì)多于一行??沒(méi)有多行被傳送,那樣會(huì)很快使程式慢下來(lái)。同樣的程式對(duì)于使用模式的類(lèi),它只需要在內(nèi)存中保留一行(Row)??其他的交給已保存的查詢資源(query resource)??換句話說(shuō),我們讓MYSQL替我們保持結(jié)果。

          接下來(lái)是視圖??我去掉了HTML以節(jié)省空間,你可以查看這篇文章的完整代碼。

          <?php
          /**
          *  Binds product data to HTML rendering
          */
          class ProductView {
          /**
          * Private
          * $model an instance of the ProductModel class
          */
          var $model;
          /**
          * Private
          * $output rendered HTML is stored here for display
          */
          var $output;
          //! A constructor.
          /**
          * Constucts a new ProductView object
          * @param $model an instance of the ProductModel class
          */
          function ProductView (&$model) {
          $this->model=& $model;
          }
          //! A manipulator
          /**
          * Builds the top of an HTML page
          * @return void
          */
          function header () {
          }
          //! A manipulator
          /**
          * Builds the bottom of an HTML page
          * @return void
          */
          function footer () {
          }
          //! A manipulator
          /**
          * Displays a single product
          * @return void
          */
          function productItem($id=1) {
          $this->model->listProduct($id);
          while ( $product=$this->model->getProduct() ) {
          // Bind data to HTML
          }
          }
          //! A manipulator
          /**
          * Builds a product table
          * @return void
          */
          function productTable($rownum=1) {
          $rowsperpage='20';
          $this->model->listProducts($rownum,$rowsperpage);
          while ( $product=$this->model->getProduct() ) {
          // Bind data to HTML
          }
          }
          //! An accessor
          /**
          * Returns the rendered HTML
          * @return string
          */
          function display () {
          return $this->output;
          }
          }
          ?>


          最后是控制器,我們將把視圖實(shí)現(xiàn)為一個(gè)子類(lèi)。

          <?php
          /**
          *  Controls the application
          */
          class ProductController extends ProductView {
          //! A constructor.
          /**
          * Constucts a new ProductController object
          * @param $model an instance of the ProductModel class
          * @param $getvars the incoming HTTP GET method variables
          */
          function ProductController (&$model,$getvars=null) {
          ProductView::ProductView($model);
          $this->header();
          switch ( $getvars['view'] ) {
          case "product":
          $this->productItem($getvars['id']);
          break;
          default:
          if ( empty ($getvars['rownum']) ) {
          $this->productTable();
          } else {
          $this->productTable($getvars['rownum']);
          }
          break;
          }
          $this->footer();
          }
          }
          ?>


          Click to fullsize


          注意這不是實(shí)現(xiàn)MVC的唯一方式??比如你可以用控制器實(shí)現(xiàn)模型同時(shí)整合視圖。這只是演示模式的一種方法。

          我們的index.php 文件看起來(lái)像這樣:

          <?php
          require_once('lib/DataAccess.php');
          require_once('lib/ProductModel.php');
          require_once('lib/ProductView.php');
          require_once('lib/ProductController.php');
          $dao=& new DataAccess ('localhost','user','pass','dbname');
          $productModel=& new ProductModel($dao);
          $productController=& new ProductController($productModel,$_GET);
          echo $productController->display();
          ?>


          漂亮而簡(jiǎn)單。

          我們有一些使用控制器的技巧,在PHP中你可以這樣做:

          $this->{$_GET['method']}($_GET['param']); 


          一個(gè)建議是你最好定義程序URL的名字空間形式(namespace),那樣它會(huì)比較規(guī)范比如:

          "index.php?class=ProductView&method=productItem&id=4"


          通過(guò)它我們可以這樣處理我們的控制器:

          $view=new $_GET['class'];
          $view->{$_GET['method']($_GET['id']);


          有時(shí)候,建立控制器是件很困難的事情,比如當(dāng)你在開(kāi)發(fā)速度和適應(yīng)性之間權(quán)衡時(shí)。一個(gè)獲得靈感的好去處是Apache group 的Java Struts,它的控制器完全是由XML文檔定義的。

          相關(guān)附件:本文完整實(shí)例

          本文英文原版地址:http://www.phppatterns.com/index.php/article/articleview/11/

          posted on 2007-10-12 14:37 風(fēng)人園 閱讀(266) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): PHP


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 胶州市| 齐齐哈尔市| 潢川县| 冷水江市| 延边| 嵊泗县| 宿迁市| 连平县| 岳西县| 上虞市| 通海县| 鹤峰县| 土默特右旗| 乐都县| 双城市| 景谷| 贡觉县| 苏尼特左旗| 正宁县| 通化市| 金川县| 华阴市| 东海县| 武城县| 石棉县| 尼玛县| 晴隆县| 福贡县| 开平市| 北碚区| 华安县| 资溪县| 运城市| 彭阳县| 合作市| 南康市| 法库县| 芦山县| 温州市| 鹤庆县| 庆元县|