# Использование

## Разбиение наборов данных на страницы

Для разбиения наборов данных на страницы Zend_Paginator должен иметь обобщенный доступ к этим данным. Поэтому доступ к данным осуществляется через адаптеры к источникам данных. В поставку Zend Framework по умолчанию входят несколько адаптеров:

Адаптеры для Zend_Paginator

Адаптер | Описание
--- | ---
Array | Использует PHP-массив
DbSelect | Использует экземпляр Zend_Db_Select, который будет возвращать массив
DbTableSelect | Использует экземпляр Zend_Db_Table_Select, который будет возвращать объект Zend_Db_Table_Rowset_Abstract. Это дает возможность получить такую дополнительную информацию, как, например, имена столбцов.
Iterator | Использует экземпляр Iterator
Null | Не использовать Zend_Paginator для разбиения на страницы. Но и в этом случае вы можете воспользоваться возможностями постраничной навигации.

Вместо извлечения всех строк, соответствующих данному запросу, адаптеры DbSelect and DbTableSelect извлекают только тот объем данных, который необходим для отображения текущей страницы.

Поэтому для определения общего количества соответствующих запросу строк динамически генерируется второй запрос. Но вы можете сами предоставить адаптеру количество строк либо запрос для его определения. См. метод setRowCount() в адаптере DbSelect для получения более подробной информации.

При создании экземпляра класса Zend_Paginator следует передавать адаптер его конструктору:

    $paginator = new Zend_Paginator(new Zend_Paginator_Adapter_Array($array));

Для удобства вы можете воспользоваться фабричным методом factory() для получения адаптеров, входящих в поставку Zend Framework:

    $paginator = Zend_Paginator::factory($array);

В случае использования адаптера Null вы должны передавать его конструктору количество элементов вместо набора данных.

Несмотря на то, что на этом этапе экземпляр формально уже пригоден к использованию, на практике вы должны будете еще передавать номер страницы, запрошенный пользователем, чтобы он мог просматривать данные:

    $paginator->setCurrentPageNumber($page);

Наиболее простым способом отслеживания номера страницы является использование URL. Мы рекомендуем использовать для этого совместимый с Zend_Controller_Router_Interface маршрутизатор, но это не является обязательным требованием.

Ниже приведен пример маршрута, который можно использовать в конфигурационном файле INI:

    routes.example.route = articles/:articleName/:page
    routes.example.defaults.controller = articles
    routes.example.defaults.action = view
    routes.example.defaults.page = 1
    routes.example.reqs.articleName = \w+
    routes.example.reqs.page = \d+

Используя этот маршрут и MVC-компоненты Zend Framework-а, вы можете устанавливать номер текущей страницы следующим образом:

    $paginator->setCurrentPageNumber($this->_getParam('page'));

Есть также другие опции, о них читайте в разделе Конфигурация.

После этого нужно присвоить экземпляр Zend_Paginator переменной вида. Если используется Zend_View с помощником действий ViewRenderer, то для этого подходит следующий код:

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

## Адаптеры DbSelect и DbTableSelect

Хотя большинство адаптеров довольно просто в использовании, адаптеры баз данных требуют дополнительных пояснений насчет извлечения данных и подсчета количества строк.

При использовании адаптеры DbSelect и DbTableSelect нет необходимости самостоятельно извлекать данные. Оба адаптера сами выполняют извлечение данных и подсчет общего количества страниц. Если полученные результаты выборки требуют дополнительной обработки, то адаптер может быть расширен с переопределением метода getItems().

Эти адаптеры не извлекают все записи из базы данных для того, чтобы посчитать их. Вместо этого адаптеры используют исходный запрос для получения соответствующего COUNT-запроса, этот запрос выполняется для получения общего количества строк. Таким образом, производится еще один дополнительный запрос к базе данных, но это во много раз быстрее, чем извлечение всего результата и использование count(), особенно в случае больших объемов данных.

Адаптеры баз данных будут пытаться строить наиболее эффективный запрос, который будет выполняться практически на всех современных СУРБД. Но в зависимости от используемой базы данных или даже выбранной структуры могут быть более эффективные пути получения количества строк. На этот случай адаптеры баз данных дают возможность устанавливать свой COUNT-запрос. Например, если вы фиксируете количество постов в блоге в отдельной таблице, то можете достичь более быстрого получения их количества, написав следующий код:

    $adapter = new Zend_Paginator_Adapter_DbSelect($db->select()->from('posts'));
    $adapter->setRowCount(
        $db->select()
           ->from(
                'item_counts',
                array(
                   Zend_Paginator_Adapter_DbSelect::ROW_COUNT_COLUMN => 'post_count'
                )
             )
    );

    $paginator = new Zend_Paginator($adapter);

Этот подход может не дать большого выигрыша в производительности в случае небольшого объема данных или простых запросов на извлечение. Однако в случае сложных запросов и больших объемов данных подобный подход может дать значительный выигрыш в производительности.

## Рендеринг страниц через скрипты видов

Для визуализации элементов страницы (если вы используете для этого Zend_Paginator) и отображения постраничной навигации используется скрипт вида.

Поскольку Zend_Paginator реализует SPL-интерфейс IteratorAggregate, то обход элементов и их отображение производится элементарно.

    <html>
    <body>
    <h1>Example</h1>
    <?php if (count($this->paginator)): ?>
    <ul>
    <?php foreach ($this->paginator as $item): ?>
      <li><?php echo $item; ?></li>
    <?php endforeach; ?>
    </ul>
    <?php endif; ?>

    <?php echo $this->paginationControl($this->paginator,
                                        'Sliding',
                                        'my_pagination_control.phtml'); ?>
    </body>
    </html>

Обратите внимание, что в конце вызывается помощник вида. PaginationControl принимает экземпляр Zend_Paginator, стиль прокрутки, скрипт вида и массив дополнительных параметров.

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

Что должно происходить, если пользователь нажимает на ссылку "next" несколько раз? Действительно, тут может быть сколько угодно вариантов. Номер текущей страницы может оставаться в середине (как это реализовано, например, в Yahoo!) или перемещаться к концу ряда при каждом нажатии "next" и затем показываться снова с другого конца. Количество отображаемых номеров страниц может даже увеличиваться и уменьшаться, пока пользователь перемещается по ним (или "прокручивает" их), как это сделано в Google.

Zend Framework предоставляет четыре стиля прокрутки:

Стили прокрутки для Zend_Paginator

Стиль прокрутки | Описание
--- | ---
All | Возвращает все страницы. Полезен для постраничной навигации в виде выпадающего списка с относительно небольшим количеством страниц. В этом случае предпочтительнее, чтобы в нем отображались все доступные страницы.
Elastic | Скроллинг в стиле Google, в котором количество отображаемых номеров страниц может увеличиваться и уменьшаться, пока пользователь перемещается по страницам.
Jumping | Пока пользователь листает страницы, номер текущей страницы перемещается к концу ряда и показывается снова в начале нового ряда.
Sliding | Скроллинг в стиле Yahoo!, в котором номер текущей страницы находится в середине ряда или настолько близко к нему, насколько это возможно. Этот стиль используется по умолчанию.

Четвертый и последний параметр предназначается для ассоциативного массива дополнительных переменных, которые должны быть доступны в вашем скрипте вида (через `$this`). Например, эти значения могут включать в себя дополнительные параметры URL для подстановки в ссылки постраничной навигации.

Установив используемые по умолчанию скрипт вида, стиль прокрутки и экземпляр вида, вы можете полностью исключить вызовы PaginationControl:

    Zend_Paginator::setDefaultScrollingStyle('Sliding');
    Zend_View_Helper_PaginationControl::setDefaultViewPartial(
        'my_pagination_control.phtml'
    );
    $paginator->setView($view);

Если все эти значения установлены, то вы можете вывести постраничную навигацию в своем скрипте вида с помощью обычной конструкции echo:

    <?php echo $this->paginator; ?>

Разумеется, можно использовать Zend_Paginator с другими шаблонизаторами. Например, со Smarty вы можете делать следующее:

    $smarty->assign('pages', $paginator->getPages());

Вы можете затем обращаться к установленным значениям из шаблона, как в коде ниже:

    {$pages->pageCount}

### Примеры постраничной навигации

Надеемся, следующие примеры помогут начать работать с Zend_Paginator:

Постраничная навигация для результатов поиска:

    <!--
    См. http://developer.yahoo.com/ypatterns/pattern.php?pattern=searchpagination
    -->

    <?php if ($this->pageCount): ?>
    <div class="paginationControl">
    <!-- Ссылка на предыдущую страницу -->
    <?php if (isset($this->previous)): ?>
      <a href="<?php echo $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="<?php echo $this->url(array('page' => $page)); ?>">
            <?php echo $page; ?>
        </a> |
      <?php else: ?>
        <?php echo $page; ?> |
      <?php endif; ?>
    <?php endforeach; ?>

    <!-- Ссылка на следующую страницу -->
    <?php if (isset($this->next)): ?>
      <a href="<?php echo $this->url(array('page' => $this->next)); ?>">
        Next &gt;
      </a>
    <?php else: ?>
      <span class="disabled">Next &gt;</span>
    <?php endif; ?>
    </div>
    <?php endif; ?>

Постраничная навигация для элементов:

    <!--
    См. http://developer.yahoo.com/ypatterns/pattern.php?pattern=itempagination
    -->

    <?php if ($this->pageCount): ?>
    <div class="paginationControl">
    <?php echo $this->firstItemNumber; ?> - <?php echo $this->lastItemNumber; ?>
    of <?php echo $this->totalItemCount; ?>

    <!-- Ссылка на первую страницу -->
    <?php if (isset($this->previous)): ?>
      <a href="<?php echo $this->url(array('page' => $this->first)); ?>">
        First
      </a> |
    <?php else: ?>
      <span class="disabled">First</span> |
    <?php endif; ?>

    <!-- Ссылка на предыдущую страницу -->
    <?php if (isset($this->previous)): ?>
      <a href="<?php echo $this->url(array('page' => $this->previous)); ?>">
        &lt; Previous
      </a> |
    <?php else: ?>
      <span class="disabled">&lt; Previous</span> |
    <?php endif; ?>

    <!-- Ссылка на следующую страницу -->
    <?php if (isset($this->next)): ?>
      <a href="<?php echo $this->url(array('page' => $this->next)); ?>">
        Next &gt;
      </a> |
    <?php else: ?>
      <span class="disabled">Next &gt;</span> |
    <?php endif; ?>

    <!-- Ссылка на последнюю страницу -->
    <?php if (isset($this->next)): ?>
      <a href="<?php echo $this->url(array('page' => $this->last)); ?>">
        Last
      </a>
    <?php else: ?>
      <span class="disabled">Last</span>
    <?php endif; ?>

    </div>
    <?php endif; ?>

Постраничная навигация в виде выпадающего списка:

    <?php if ($this->pageCount): ?>
    <select id="paginationControl" size="1">
    <?php foreach ($this->pagesInRange as $page): ?>
      <?php $selected = ($page == $this->current) ? ' selected="selected"' : ''; ?>
      <option value="<?php
            echo $this->url(array('page' => $page));?>"<?php echo $selected ?>>
        <?php echo $page; ?>
      </option>
    <?php endforeach; ?>
    </select>
    <?php endif; ?>

    <script type="text/javascript"
         src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js">
    </script>
    <script type="text/javascript">
    $('paginationControl').observe('change', function() {
        window.location = this.options[this.selectedIndex].value;
    })
    </script>

### Список свойств

Следующие опции доступны внутри скрипта вида для постраничной навигации:

Свойства, доступные в скрипте вида

Свойство | Тип | Описание
--- | --- | ---
first | integer | Номер первой страницы (т.е. 1)
firstItemNumber | integer | Действительный номер первого элемента на текущей странице
firstPageInRange | integer | Первая страница в ряде, возвращенном текущим стилем прокрутки
current | integer | Текущий номер страницы
currentItemCount | integer | Количество элементов на текущей странице
itemCountPerPage | integer | Максимальное количество отображаемых на странице элементов
last | integer | Номер последней страницы
lastItemNumber | integer | Действительный номер последнего элемента на текущей странице
lastPageInRange | integer | Последняя страница в ряде, возвращенном текущим стилем прокрутки
next | integer | Номер следующей страницы
pageCount | integer | Общее количество страниц
pagesInRange | array | Массив страниц, возвращенный текущим стилем прокрутки
previous | integer | Номер предыдущей страницы
totalItemCount | integer | Общее количество элементов