Пагинация в богатой доменной модели - PullRequest
3 голосов
/ 21 мая 2010

Я использую модель богатых доменов в своем приложении.Основные идеи были взяты там .Например, у меня есть объекты 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 {

}
?>

Функция getUserComments Mapper просто возвращает что-то вроде:

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

, который является массивом.fetchAllByUserId принимает параметры $ count и $ offset, но я не знаю, чтобы передать их из моего контроллера в эту функцию через модель без перезаписи всего кода модели.

Поэтому вопрос заключается в том, как организовать разбиение на страницы через модельданные (getComments).Есть ли "красивый" метод для получения комментариев от 5 до 10, а не для всех, так как getComments возвращает по умолчанию.

Ответы [ 4 ]

2 голосов
/ 22 мая 2010

Если вы заботитесь только о разбиении на страницы результатов, которые видит пользователь, и не заботитесь об улучшении своей производительности, вы, вероятно, можете избежать встраивания нумерации страниц в инфраструктуру модели.

Предполагая, что у вас есть какой-то класс 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 голосов
/ 22 июня 2010

Думая об этом тоже прямо сейчас. Вы уже нашли подходящее решение? Мои мысли до сих пор 1) избегайте использования чего-либо вроде Zend_Paginator и просто делайте все самостоятельно . Т.е. вы получаете параметры из среды (объект запроса или некоторый файл конфигурации и т. д.), например itemCountPerPage, currentPageNumbe, вы передаете их в метод уровня обслуживания (или в вашем случае это просто маппер), например

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

затем вы запрашиваете общую сумму товара у своего маппера (это полностью зависит от вас, делать это в отдельном запросе или нет), например

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

Так что теперь у вас есть все данные, чтобы сделать «представление управления разбиением на страницы». Говоря «Zendy», вы передаете все эти переменные в представление и сами выполняете управление разбиением на страницы.

2) Разработать адаптер для Zend_Paginator , который бы ЛИГЛ о его состоянии. То есть вместо того, чтобы иметь все элементы, он просто будет иметь некоторую часть, которая вам нужна, но вы вручную установите значение «общее количество» и другие. Мне не очень нравится такой подход.

3) Добавьте некоторый специальный объект домена под названием «Страница», который будет возвращен из картографов или сервисных слоев. Такой объект будет инкапсулировать все те переменные, которые нам нужны для построения представления управления разбиением на страницы. При этом мы сможем сделать следующее:

` $ commentsService = $ this-> getCommentsService (); $ CommentsService-> setItemCountPerPage (10); // скажем, 10 взято из некоторого config.ini

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

`

Где-то в представлении мы можем получить эти ` $ CommentsPage-> GetItems (); // коллекция комментариев

$ commentsPage-> getCurrentPageNumber ();

$ commentsPage-> getTotalItemsCount ();

`

, что достаточно для создания элемента управления нумерацией страниц.

Это все.

В настоящее время я выбираю между первым и третьим подходами, вероятно, пойдет с третьим.

2 голосов
/ 22 мая 2010

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() частичное (взято из этого сообщения в блоге ):

<?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; ?>

Дополнительные примеры Zend_Paginator доступны через поиск в Google.

0 голосов
/ 22 июня 2010

Вот мое решение:

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

Модель запрашивает объект Zend_Paginate с подготовленным запросом на основе user_id, предоставленного из Model. Затем Model добавил фильтр в объект Zend_Paginate, чтобы сделать его совместимым с другими методами Model (возвращать классы Model_Collection, а не массивы). Все остальные параметры пагинации устанавливаются в контроллере (страница, количество элементов на странице и т. Д.).

Вот как я разделил хранилище, бизнес и логику управления в своем приложении.

...