2008-12-05 35 views
6

我正在使用Zend Framework(1.7)項目,其結構鬆散地基於快速啓動應用程序的結構 - 前端控制器,動作控制器,使用Zend_Db_Table訪問數據庫的視圖&模型。我的一個主要模型依賴於一些昂貴的連接來提取其主要列表,所以我正在研究使用Zend_Paginator來減少從數據庫中返回的行數。我的問題是,Zend_Paginator只有4個適配器,其中沒有一個真的適合我。Zend_Paginator模糊MVC行

  • 陣列:構建陣列喂到ZP將涉及獲取所有記錄這正是我想避免
  • 迭代:啞迭代器將提出同樣的問題作爲一個數組和一個聰明的人覺得這將是一個不適合模型
  • DbSelect:獲取一個DbSelect對象直到控制器會不自然地將控制器綁定到我的數據庫的內部工作(更不用說產生原始結果行而不是封裝的對象)
  • DbTableSelect:同DbSelect
  • 空適配器:通過所有細節來回手動。

將paginator傳遞到模型中,感覺它也會違反MVC分離。問題是我錯誤地構建了我的模型,我爲了維護MVC分離而教條主義,還是錯過了將所有運動部件粘在一起的乾淨優雅的方式?

回答

2

您可以提供您的模型接受$current_page$per_page參數和返回當前頁面的數據集,以及一個分頁程序對象的接口。

通過這種方式,您的所有分頁代碼都包含在模型中,您可以自由使用Db適配器,而不會感覺您已經破壞了概念。

另外,控制器真的不應該設置尋呼機,因爲你是正確的,它被綁定到數據(和模型是爲數據,而不僅僅是數據庫連接)。

class Model 
{ 
    //... 
    /** 
    * @return array Array in the form: array('paginator' => obj, 'resultset' => obj) 
    */ 
    public function getAll($where = array(), $current_page = null, $per_page = null); 
    //... 
} 
0

嗯,我不能給你一個關於使用DbSelect的問題的答案,但我確實遇到了有關減少行數的問題(在ibuildings博客的評論中)。可能對一些讀者有用。

$select = $db->from('users')->order('name');  
$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_DbSelect($select)); 
$paginator->setCurrentPageNumber($this->_getParam('page', 1)); 
$paginator->setItemCountPerPage(25); 
$this->view->paginator = $paginator; 
0

使用MVC時需要考慮的一個重要考慮因素是該模型適用於所有域邏輯,而控制器適用於業務邏輯。一般的經驗法則是模型應該不知道接口(控制器或視圖),但它們不需要是簡單的DB訪問器。爲了儘可能便攜,他們不應該知道格式化或顯示屬性(除非它是域邏輯的一部分)。實際上,操縱領域邏輯的所有邏輯都應該在模型中,而不是在控制器中。控制器應該從界面傳遞信息,根據需要將其轉換爲模型,並選擇要顯示/更新的視圖。如果它與接口沒有任何關係,那麼它最好在模型中表示,而不是在控制器中表示,因爲如果您稍後決定換出控制器/視圖對,它將允許更多的代碼重用。

理想情況下,您的模型應該提供一個用於訪問所需信息的界面。只要模型不知道MVC的VC部分,那麼在接口後面實現的方式並不是MVC的關注點。如果這意味着傳遞paginator對象,這並不是直接違反MVC原則,但如果paginator與渲染本身有關(對不起,我不知道Zend),最好傳遞一個接口(缺少渲染方法),讓模型操作/填充它,然後將其傳回。這樣你就沒有從模型中生成渲染代碼,並且如果你決定稍後將應用程序變成控制檯應用程序(或添加某種API接口),則可以替換Paginator實現。

0

如果您使用DbSelect適配器,您可以簡單地傳入結果集,這在保持某種分離方面有很長的路要走。所以,在你的控制器:

$items = new Items();//setup model as usual in controller 
$this->view->paginator = Zend_Paginator::factory($items->getAll()); //initialize the pagination in the view NB getAll is just a custom function to encapsulate my query in the model that returns a Zend_Db_Table_Rowset 
$this->view->paginator->setCurrentPageNumber($page); //$page is just the page number that could be passed in as a param in the request 
$this->view->paginator->setView($this->view); 

在視圖中,您可以通過分頁程序

<?php foreach($this->paginator as $item):?> 
<?=$item->someProperty?> 
<?php endforeach;?> 

這是一個簡單的例子訪問數據(我還設置了默認滾動的風格和默認視圖在引導部分),但是我在控制器中設置它並不壞,因爲從模型中檢索的數據無論如何都被Controller放置在視圖中,並且此實現使用結果集NOT模型。

+1

但不這似乎浪費了?調用getAll() - 一個昂貴的調用,可能會返回數千或數百萬條記錄 - 然後忽略20個左右,但您可能需要一頁顯示的數據? – 2010-10-20 14:28:53

2

現在有Zend_Paginator的一個設置篩選方法,可以讓你從該行對象中的數據加載到任何你想要的模型對象:

class Model_UserDataMapper { 
    public function getUsers($select, $page) { 
     $pager = Zend_Paginator::factory($select); 
     $pager->setItemCountPerPage(10) 
        >setCurrentPageNumber($page) 
        ->setFilter(new Zend_Filter_Callback(array($this,'getUserObjects'))); 
    } 

    public function getUserObjects($rows) { 
     $users = array(); 

     foreach($rows as $row) { 
      $user = new Model_User($row->toArray()); 

      $users[] = $user; 
     } 

     return $users; 
    } 
} 
0

你也可以直接實現Zend_Paginator_Adapter_Interface或以任何模式擴展Zend_Paginator_Adapter_DbSelect需要支持分頁。

這樣,模型並不直接瞭解任何關於View,Controller甚至Zend_Paginator的內容,但可以直接與Zend_Paginator一起使用,只要它最有意義即可。

class ModelSet extends Zend_Paginator_Adapter_DbSelect 
{ 
    public function __construct(...) 
    { 
     // Create a new Zend_Db_Select ($select) representing the desired 
     // data set using incoming criteria 
     parent::__construct($select); 
    } 
    ... 
} 

像這樣的東西,你可以使用這個類,只要它使最有意義的一個實例,直接實例化一個尋呼機:

$modelSet = new ModelSet(...); 
... 
$pager = new Zend_Paginator($modelSet); 
$pager->setItemCountPerPage(...); 
$pager->setCurrentPageNumber(...); 
... 
// The first time the record set is actually retrieved will be at the beginning 
// of the first traversal 
foreach ($pager as $record) 
{ 
    // ... do stuff with the record ... 
} 

現在,你可以使用這個類作爲基類任何「模型」是一組。

1

真的需要一個解決方案中,我可以使用類的Zend_Db_Table方法,爲我的分頁程序適配器的資源,而不是一個數組或對象Zend_Db_Select對象。

這種高級建模與Zend_Paginator的標準適配器不兼容。我繼續爲所有渴望得到答案的人解決這個問題,就像我一樣。

<?php 

    /* /zend/Paginator/Adapter/DbTableMethod.php */  
    class Zend_Paginator_Adapter_DbTableMethod implements Zend_Paginator_Adapter_Interface { 

     protected $_class; 
     protected $_method; 
     protected $_parameters; 
     protected $_rowCount = null; 

     public function __construct($class, $method, array $parameters = array()){ 

     $reflectionClass = new ReflectionClass($class); 
     $reflectionMethod = $reflectionClass->getMethod($method); 
     $reflectionParameters = $reflectionMethod->getParameters(); 

     $_parameters = array(); 

     foreach ($reflectionParameters as $reflectionParameter){ 

      $_parameters[$reflectionParameter->name] = ($reflectionParameter->isDefaultValueAvailable()) ? $reflectionParameter->getDefaultValue() : null; 

     }  

     foreach ($parameters as $parameterName => $parameterValue){ 

      if (array_key_exists($parameterName, $_parameters)) $_parameters[$parameterName] = $parameterValue; 

     } 

     $this->_class = $class; 
     $this->_method = $method; 
     $this->_parameters = $_parameters; 

     } 

     public function count(){ 

      if (is_null($this->_rowCount)){ 

       $parameters = $this->_parameters; 
       $parameters['count'] = true; 

       $this->_rowCount = call_user_func_array(array($this->_class, $this->_method), $parameters); 

      }  

      return $this->_rowCount; 

     } 

     public function getItems($offset, $itemCountPerPage){ 

      $parameters = $this->_parameters; 
      $parameters['limit'] = $itemCountPerPage; 
      $parameters['offset'] = $offset; 

      $items = call_user_func_array(array($this->_class, $this->_method), $parameters); 

      return $items; 
     } 

    } 

?> 

這是它如何在您的控制器:

<?php 

    class StoreController extends Zend_Controller_Action { 

     public function storeCustomersAction(){ 

      $model = new Default_Model_Store(); 
      $method = 'getStoreCustomers'; 
      $parameters = array('storeId' => 1); 

      $paginator = new Zend_Paginator(new Site_Paginator_Adapter_DbTableMethod($model, $method, $parameters)); 
      $paginator->setCurrentPageNumber($this->_request->getParam('page', 1)); 
      $paginator->setItemCountPerPage(20); 

      $this->view->paginator = $paginator; 

     } 

    } 

?> 

此適配器工作的唯一要求是,以任何順序列出你的模型方法的參數列表如下參數([適配器將檢測通過反射方法簽名):

$極限= 0,$偏移量= 0,$計數=假

paginator會用$ limit,$ offset和$ count參數的相應值調用你的方法。而已!

例子:

 <?php 

     class Default_Model_Store extends Zend_Db_Table { 

     public function getStoreCustomers($storeId, $includeCustomerOrders = false, $limit = 0, $offset = 0, $count = false){ 

if ($count) /* return SELECT COUNT(*) ... */ 

       /* ... run primary query, get result */ 
       $select = $this->_db->select(...)->limit($limit, $offset); 


       $rows = $this->_db->fetchAll($select); 

       if ($includeCustomerOrders){ 

        foreach ($rows as &$row){ 

         $customerId = $row['id']; 
         $row['orders'] = $this->getCustomerOrders($customerId); 

        } 

       } 

       return $rows;  

      } 

     } 

    ?>