富域模型中的分页

时间:2010-05-21 16:42:35

标签: zend-framework model domain-driven-design

我在我的应用中使用了丰富的域名模型。基本想法是there。例如,我有User和Comment实体。它们定义如下:

<?php
class Model_User extends Model_Abstract {

    public function getComments() {
        /**
        * @var Model_Mapper_Db_Comment
        */
        $mapper = $this->getMapper();
        $commentsBlob = $mapper->getUserComments($this->getId());
        return new Model_Collection_Comments($commentsBlob);
    }

}

class Model_Mapper_Db_Comment extends Model_Mapper_Db_Abstract {

    const TABLE_NAME = 'comments';

    protected $_mapperTableName = self::TABLE_NAME;

    public function getUserComments($user_id) {
        $commentsBlob = $this->_getTable()->fetchAllByUserId((int)$user_id);
        return $commentsBlob->toArray();
    }
}

class Model_Comment extends Model_Abstract {

}
?>

Mapper的getUserComments函数只返回如下内容:

return $this->getTable->fetchAllByUserId($user_id)

是数组。 fetchAllByUserId接受$ count和$ offset params,但是我不知道通过模型将它们从我的Controller传递给这个函数而不重写所有的模型代码。

所以问题是如何通过模型数据(getComments)组织分页。是否有一个“美丽”的方法来获取5到10之间的评论,而不是全部,因为getComments默认返回。

4 个答案:

答案 0 :(得分:2)

Zend_Paginator可能是您正在寻找的简单解决方案。它可能需要array()Iterator的实例(Zend_Db_Table_Rowset

$paginator = Zend_Paginator::factory($model->getComments());
$paginator->setItemCountPerPage(5);
$paginator->setCurrentPageNumber($this->getRequest()->getParam('page',1));
$this->view->comments = $paginator;

在视图中:

<?php foreach($this->comments as $comment): ?>
   Render your HTML for the comment
<?php endforeach; ?>
<?php echo $this->paginationControl($this->comments, 'Sliding', '_pagination.phtml'); ?>

一个(非常)简单paginationControl()部分(取自this blog post):

<?php if ($this->pageCount): ?>
<div class="paginationControl">
  <?php if (isset($this->previous)): ?>
    <a href="<?= $this->url(array(’page’ => $this->previous)); ?>">&lt; Previous</a> |
  <?php else: ?>
    <span class="disabled">&lt; Previous</span> |
  <?php endif; ?>

  <?php foreach ($this->pagesInRange as $page): ?>
    <?php if ($page != $this->current): ?>
      <a href="<?= $this->url(array(’page’ => $page)); ?>"><?= $page; ?></a> |
    <?php else: ?>
      <?= $page; ?> |
    <?php endif; ?>
  <?php endforeach; ?>

  <?php if (isset($this->next)): ?>
    <a href="<?= $this->url(array(’page’ => $this->next)); ?>">Next &gt;</a>
  <?php else: ?>
    <span class="disabled">Next &gt;</span>
  <?php endif; ?>
</div>
<?php endif; ?>

More examples of Zend_Paginator可通过谷歌搜索获得。

答案 1 :(得分:2)

如果您只关心对用户看到的结果进行分页,并且不关心提高性能,则可以避免在模型基础结构中构建分页。

假设你有一些Model_Collection_Abstract类,你的所有集合类都来自它,你可能会破坏它的分页。

那么你的代码看起来像是:

<?PHP
    //$comments is a subclass of Model_Collection_Abstract, which implements the paging stuff
    $comments = $user->getComments(); 
    $comments->setStart(10);
    $comments->setPageLength(10);

    $numPages = $comments->numPages(); //can be derived from the pagelength and the collection's internal record store.

   $currentPage = $comments->currentPage(); //can be derived from start and page length

    foreach($comments as $comment){
       //this code runs up to ten times, starting at the tenth element in the collection.
    }

这里的缺点是你总是抓住所有的评论,即使你只想看到其中的十条。但这对你来说可能是一个可以接受的解决方案。

如果您只想从数据库中提取N条记录(N =要显示的数字),那么当然您需要实现一些方法来传递开始/限制或等效参数,一直到您的模型。

编辑:关于查看Zend_Paginator的另一个答案也值得一读。如果您的集合类implements Iterator,很可能您可以将其插入Zend_Paginator并保存一些严重的麻烦。我还没有这样做(但是),但值得一看!

答案 2 :(得分:2)

现在想一想。你找到了一个简洁的解决方案吗? 到目前为止我的想法是 1)避免使用像Zend_Paginator这样的东西,只做你自己的一切。即你从环境(请求对象或一些配置文件等)获取params,如itemCountPerPage,currentPageNumbe,你将它们传递到你的服务层方法(或者在你的情况下,它只是映射器),如

$comments = $this->getCommentsMapper()->getPage($itemCountPerPage, $currentPage);

然后你从你的映射器请求总项目金额(如果你应该在单独的请求中完成,那么完全由你决定),如

$totalComments = $this->getCommentsMapper()->getTotal();

所以现在你有了所有数据来制作“分页控制视图”。说到“Zendy”,你将所有这些变量传递给视图并自己绘制分页控件。

2)为Zend_Paginator 开发一个适用于LIE的适配器。即而不是拥有所有项目,它将只有你需要的一部分,但你将手动设置“总计数”值和其他。我自己并不喜欢这种方法。

3)添加一些名为“Page”的特殊域对象,该对象将从映射器或服务层返回。这样的对象将封装我们构建分页控件表示所需的所有变量。有了这个,我们就可以做到以下几点:

` $ commentsService = $ this-&gt; getCommentsService(); $ commentsService-&GT; setItemCountPerPage(10); //假设10取自一些config.ini

$ commentsPage = $ this-&gt; getCommentsService() - &gt; getPage(12); $ this-&gt; view-&gt; commentsPage = $ commentsPage;

`

在视图的某个地方我们可以得到这些 ` $ commentsPage-&GT; getItems(); //评论集合

$ commentsPage-&GT; getCurrentPageNumber();

$ commentsPage-&GT; getTotalItemsCount();

`

这足以构建分页控件。

这一切。

我目前正在选择第一种和第三种方法,可能会选择第三种方式。

答案 3 :(得分:0)

这是我的解决方案:

class Model_Mapper_Db_Comment extends Model_Mapper_Db_Abstract {

    public function getUserCommentsPaginator($user_id) {
        $select = $this->_getTable()->select()->where('user_id = ?', (int)$user_id);
        $paginator = Zend_Paginator::factory($select, 'DbSelect');
        return $paginator;
    }
} 

class Model_User extends Model_Abstract implements Zend_Auth_Adapter_Interface {

    public function getCommentsPaginator() {
        $paginator = $this->getMapper(null, 'Comment')->getUserCommentsPaginator($this->id);
        $paginator->setFilter(new App_Filter_Array_Collection('Model_Collection_Comments'));
        return $paginator;
    }
}

模型根据Model提供的user_id请求Zend_Paginate对象和准备好的查询。然后Model将过滤器添加到Zend_Paginate对象,使其与其他Model方法兼容(返回Model_Collection类,而不是数组)。所有其他分页参数都在控制器中设置(页面,每页的项目数等)。

这就是我在应用程序中分离存储,业务和控制逻辑的方式。