Статьи

Изучение Zend_Paginator

Одна из областей отображения списков на веб-страницах, которую я обычно не любил делать, — это разбиение на страницы, так как это немного ошибочно. Недавно мне нужно было сделать именно это, хотя я не мог делегировать это, так как мой коллега был слишком занят другой работой. В результате я подумал, что на этот раз мне стоит заглянуть в Zend_Paginator . Оказывается, это действительно удобно, и документация тоже великолепна.

Действительно полезная вещь в Zend_Paginator заключается в том, что он использует адаптеры для сбора своих данных. Существует множество адаптеров, включая массив, dbSelect, dbTableSelect и итератор. Для меня интересными являются dbSelect и dbTableSelect, так как я использую слои доступа к данным на основе Zend_Db.

Вот как я использовал его с картографом данных на основе Zend_Db в TodoIt.

Настройка пагинатора

Мой текущий метод выглядит так:

class Application_Model_TaskMapper
{
public function fetchOutstanding()
{
$db = $this->getDbAdapter();
$select = $db->select();
$select->from($this->_tableName);
$select->where('date_completed IS NULL');
$select->order(array('due_date ASC', 'id DESC'));
$rows = $db->fetchAll($select);
foreach ($rows as $row) {
$task = new Application_Model_Task($row);
$tasks[] = $task;
}
return $tasks;
}

// etc

Это довольно стандартный код для отображения данных. Мы выбираем данные из базы данных и конвертируем их в массив объектов. Для того чтобы paginator мог выполнять свои функции, мы должны передать ему объект select, чтобы он мог установить limit () для объекта select.

Код становится:

public function fetchOutstanding()
{
$db = $this->getDbAdapter();
$select = $db->select();
$select->from($this->_tableName);
$select->where('date_completed IS NULL');
$select->order(array('date_completed DESC', 'id DESC'));

$adapter = new Zend_Paginator_Adapter_DbSelect($select);
$paginator = new Zend_Paginator($adapter);
return $paginator;
}

Как видите, мы создаем экземпляр Zend_Paginator_Adapter_DbSelect, который принимает объект $ select, создает экземпляр Zend_Paginator и возвращает его. Объект Zend_Paginator реализует Interator, поэтому вы можете использовать его точно так же, как массив в цикле foreach, и, следовательно, в теории ваш сценарий представления не нуждается в изменении.

Однако код, который использует TaskMapper, ожидает массив объектов Task, а не массив массивов. Чтобы сообщить пагинатору о создании наших объектов, мы расширяем Zend_Paginator_Adapter_DbSelect и переопределяем getItems () следующим образом:

class Application_Model_Paginator_TaskAdapter extends Zend_Paginator_Adapter_DbSelect
{
/**
* Returns an array of items for a page.
*
* @param integer $offset Page offset
* @param integer $itemCountPerPage Number of items per page
* @return array
*/
public function getItems($offset, $itemCountPerPage)
{
$rows = parent::getItems($offset, $itemCountPerPage);

$tasks = array();
foreach ($rows as $row) {
$task = new Application_Model_Task($row);
$tasks[] = $task;
}
return $tasks;
}
}

Здесь мы использовали код создания сущности, который был в нашей первоначальной реализации fetchOutstanding (), и поместили его в getItems ().

Очевидно, что мы должны обновить fetchOutstanding (), чтобы использовать наш новый адаптер, поэтому мы заменим

$adapter = new Zend_Paginator_Adapter_DbSelect($select);

с

$adapter = new Application_Model_Paginator_TaskAdapter($select);

 

Теперь, когда мы перебираем объект пагинации, мы получаем экземпляры Task, и все в порядке с миром.

Использование пагинатора

Теперь, когда у нас есть пагинатор, нам нужно его использовать. В частности, нам нужно сообщить paginator, какой номер страницы мы хотим просмотреть и сколько элементов на странице. В TodoIt это делается в объекте ServiceLayer и выглядит примерно так:

class Application_Service_TaskService
{
// ...

public function fetchOutstanding($page, $numberPerPage = 25)
{
$mapper = new Application_Model_TaskMapper($acl);
$tasks = $mapper->fetchOutstanding();
$tasks->setCurrentPageNumber($page);
$tasks->setItemCountPerPage($numberPerPage);
return $tasks;
}

// ...

Очевидно, что параметр $ page приходит через URL в какой-то момент, поэтому контроллер выглядит примерно так:

class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$page = $this->_getParam('page', 1);

$taskService = new Application_Service_TaskService();
$this->view->outstandingTasks = $taskService->fetchOutstanding($page);

$messenger = $this->_helper->flashMessenger;
$this->view->messages = $messenger->getMessages();
}

//...

и тогда представление использует foreach, как и следовало ожидать.

Добавление элементов управления подкачкой

Наконец, чтобы завершить постраничный список, мы должны предоставить пользователю механизм для выбора следующей и предыдущей страниц, а также, возможно, перехода на определенную страницу. Это делается с помощью отдельного скрипта вида, который вы передаете в paginator. В вашем скрипте вида вы помещаете что-то вроде:

<?php echo $this->paginationControl($this-> outstandingTasks,
'Sliding',
'pagination_control.phtml'); ?>

Первый параметр — это ваш объект для разбивки на страницы. Второй — это «стиль прокрутки». В руководстве описаны четыре варианта : Все , Эластичный , Прыжки и Скользящие . Лично я решил не отображать номера страниц сами, поэтому не имеет значения, какой из них я выберу. Последний параметр — это скрипт частичного просмотра, который вы хотите визуализировать. Это позволяет вам полностью настроить HTML.

Вот то, что я использую, на котором в значительной степени основан и пример в документации:

<?php if ($this->pageCount): ?>
<div class="pagination-control">
<!-- Previous page link -->
<?php if (isset($this->previous)): ?>
<a href="<?php echo $this->url(array('page' => $this->previous)); ?>">
Previous
</a> |
<?php else: ?>
<span class="disabled">< Previous</span> |
<?php endif; ?>

<!-- Next page link -->
<?php if (isset($this->next)): ?>
<a href="<?php echo $this->url(array('page' => $this->next)); ?>">
Next >
</a>
<?php else: ?>
<span class="disabled">Next ></span>
<?php endif; ?>
<span class="pagecount">
Page <?php echo $this->current; ?> of <?php echo $this->pageCount; ?>
</span>
</div>
<?php endif; ?>

Вот и все; Теперь у меня есть задачи с разбивкой по страницам в TodoIt, и, как вы можете видеть, Zend_Paginator очень прост в использовании и, что более важно, прост в настройке под ваши собственные нужды.