代码之家  ›  专栏  ›  技术社区  ›  Sean McSomething

Zend_Paginator模糊MVC线条

  •  6
  • Sean McSomething  · 技术社区  · 17 年前

    我正在开发一个Zend Framework(1.7)项目,其结构松散地基于快速入门应用程序的结构——前端控制器、动作控制器、视图和;使用Zend_Db_Table访问数据库的模型。我的一个主要模型依赖于一些昂贵的连接来提取其主要列表,因此我正在考虑使用Zend_Paginator来减少从数据库中带回的行数。我的问题是Zend_Paginator只有4个适配器,似乎都不适合我。

    • 阵列 :构建要馈送给ZP的数组将涉及获取所有记录,这是我试图避免的
    • 迭代器 :一个愚蠢的迭代器会出现与数组相同的问题,而一个聪明的迭代器会觉得它不适合Model
    • DbSelect :将一个DbSelect对象连接到控制器会让控制器与数据库的内部工作不舒服地联系在一起(更不用说产生原始结果行而不是封装对象了)
    • DbTableSelect :与DbSelect相同
    • 空适配器 :手动来回传递所有细节。

    将分页器传递到模型中感觉也会违反MVC分离。问题是我构建的模型不正确,我对保持MVC分离过于教条,还是我缺少一种干净、优雅的方式将所有移动部件粘在一起?

    7 回复  |  直到 17 年前
        1
  •  2
  •   dcousineau    17 年前

    您可以在模型上提供一个界面,该界面接受 $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 );
        //...
    }
    
        2
  •  2
  •   Troy    16 年前

    Zend_Paginator现在有一个setFilter方法,允许您将数据从行对象加载到所需的任何模型对象:

    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;
        }
    }
    
        3
  •  1
  •   axiom82 Joeytje50    12 年前

    一、 真正地 我需要一个解决方案,其中我可以使用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;
    
            }
    
        }
    
    ?>
    

    此适配器工作的唯一要求是在模型方法参数列表中列出以下参数(以任何顺序[适配器将通过反射检测方法签名):

    $limit=0,$offset=0,$count=false

    分页器将使用$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;    
    
                }
    
            }
    
        ?>
    
        4
  •  0
  •   rg88    17 年前

    好吧,我无法回答你对使用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;
    
        5
  •  0
  •   Sydius    17 年前

    使用MVC时需要考虑的一个重要因素是,模型适用于所有域逻辑,而控制器适用于业务逻辑。一般经验法则是,模型不应该知道接口(控制器或视图),但它们不需要是简单的数据库访问器。为了尽可能便携,他们也应该对格式或显示属性一无所知(除非这是域逻辑的一部分)。

    事实上,所有操纵域逻辑的逻辑都应该在模型中,而不是在控制器中。控制器应将信息从界面传递到模型,根据需要进行转换,并选择要显示/更新的视图。如果它与接口无关,那么在模型中表示它可能比在控制器中更好,因为如果你以后决定交换控制器/视图配对,它将允许更多的代码重用。

    理想情况下,您的模型应该提供一个访问所需信息的接口。只要模型不知道MVC的VC部分,那么如何在该接口后面实现就不是MVC关心的问题。如果这意味着传递一个分页器对象,这并没有直接违反MVC原则,但如果分页器与渲染本身有关(抱歉,我不知道Zend),最好传入它的接口(缺少渲染方法),让模型操纵/填充它,然后将其传回。这样,您就不会从模型中生成渲染代码,并且如果您决定稍后将应用程序作为控制台应用程序(或添加某种API接口),则可以替换paginator实现。

        6
  •  0
  •   Akeem    17 年前

    如果你使用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;?>
    

    这是一个简化的示例(我还在引导程序中设置了默认滚动样式和默认视图部分),但我认为在控制器中设置它还不错,因为从模型中检索到的数据无论如何都是由控制器放置在视图中的,并且此实现使用的是结果集而不是模型。

        7
  •  0
  •   Elmo    16 年前

    您还可以直接实现Zend_Paginator_Adapter_Interface,或者在需要支持分页的任何模型中扩展Zend_Pagitor_Adapter_DbSelect。

    这样,模型就不会直接了解视图、控制器甚至Zend_Paginator,但可以在最有意义的地方直接与Zend_Pagulator一起使用。

    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 ...
    }
    

    现在,您可以将此类用作任何作为集合的“模型”的基类。

    推荐文章