pagination.ru

Решение на PHP5 для быстрого создания постраничной разбивки данных веб-приложений

Содержание

Что это и зачем это нужно?

Pagination.ru — это простое в установке и настройке решение на языке PHP (не ниже версии 5.3 у вас версия ниже?), позволяющее быстрое создание т.н. пагинации (от английского слова pagination — порядковая нумерация страниц) — разбивки большого количества информации из таблицы реляционной базы данных на небольшие виртуальные страницы, а также создание визуального интерфейса на языке HTML, позволяющего делать переход по этим страницам.

Пагинация реализуется программистами при разработке гостевых книг, форумов, каталогов объявлений, страниц комментариев, списков статей и в любых других случаях, когда из общего массива данных, хранящихся в СУБД, требуется выбрать ограниченный набор информации, определенный некоторыми рамками.

Если вы — программист, разрабатывающий продукт требующий разбивки информации из базы данных на страницы, то решение от Pagination.ru — это то, что вам нужно!

Небольшое вступление от автора

Данное решение было создано мной на заре моего увлечения веб-программированием много лет назад и до сих пор продолжает работать, я постоянно использую эту наработку в своих программах и ещё ни разу данный код меня не подводил. Поэтому я решил поделиться этим решением с другими программистами. Я надеюсь, код данного проекта избавит вас от рутинной работы написания подобного аналога.

Хочу отметить, что основную логику кода спустя много лет я так и не смог подвергнуть качественному рефакторингу. Логика кода осталась такой, какой я её создал много лет назад и модифицировать её без причины я не стану — незачем. Хотя признаюсь, я уже давно перестал понимать, как это всё работает. Но это работает!

Краткий обзор возможностей

Данный продукт позволяет делать разбивку «на страницы» данных из СУБД, а также создавать интерфейс управления страницами. Интерфейс управления страницами может быть трёх видов:

Стандартный тип интерфейса
pagination

Наиболее распространён в интернете.

Инкрементный тип интерфейса
pagination

Якоря гиперссылок на страницы содержат числовой диапазон, означающий те записи в порядке следования, которые будут выведены при открытии данной гиперссылки. Инкрементным данный тип интерфейса называется потому, что диапазоны увеличиваются по мере удаления от начала данных.

Декрементный тип интерфейса
pagination

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

Думаю, опытный пользователь интернета, а уж тем более веб-программист (а вы наверное таковым и являетесь) без труда, интуитивно поймет предназначение каждой кнопки-ссылки подобных интерфейсов. Но на всякий случай, я опишу всю эту функциональность на примере Стандартного типа (п. 1) интерфейса, когда открыта страница №14 (рис. 1):

Аналогично:

Через API можно проводить следующие манипуляции с интерфейсом управления страницами:

Подключение и настройка Pagination.ru от «А» до «Я»

Работа с контроллером

Итак, наша задача создать постраничную выборку данных, иногда её ещё называют «отстраничиватель» или просто «пагинатор». Скачайте архив с кодом, распакуйте его и положите два php-файла из архива в директорию `Pagination`. Подключите php-код в вашу PHP-страницу, на которой предполагается постраничный вывод информации из базы:

<?php
include_once("./Pagination/Manager.php");
include_once(
"./Pagination/Helper.php");

Теперь создайте подключение к вашей СУБД посредством стандартных функций PHP для работы с базой данных или с помощью вашей любимой DAO-обёртки. В примере я буду использовать функции PHP расширения mysql.

$db mysql_connect("localhost""root""");
mysql_select_db("test"$db);

Теперь, самое интересное. Допустим, у нас есть таблица `adverts` содержащая несколько тысяч записей — объявлений, оставленных пользователями. На одной странице мы хотим выводить по 10 записей из таблицы `adverts`, а под записями мы хотим расположить интерфейс, содержащий 5 ссылок на последующие страницы. Инстанцируем объект класса `Pagination_Manager`, указав первым аргументом конструктора число 10, вторым — число 5, а третьим — суперглобальный ассоциативный массив $_REQUEST (также можно было указать $_GET):

$paginationManager = new Pagination_Manager(105$_REQUEST);

Отлично. Теперь необходимо сделать запрос к вашей СУБД, к таблице `adverts` и получить первые записи. Я надеюсь, вы знаете, как писать SQL запросы на выборку данных и знакомы с выражением LIMIT.

$sql "SELECT
           SQL_CALC_FOUND_ROWS
           `message`
        FROM
           `adverts`
        ORDER BY
           `id` DESC
        LIMIT " 
.
           
$paginationManager->getStartLimit() . "," .
           
$paginationManager->getStopLimit();

Как видно из примера, с помощью объекта $paginationManager и его методов getStartLimit() и getStopLimit() мы получили два значения, которые и подставили в качестве значений для выражения LIMIT. Объект $paginationManager взял на себя работу по вычислению того, какие данные должны возвратить методы getStartLimit() и getStopLimit(), всё, что требовалось от нас — это написать стандартный SQL запрос на выборку данных и всё!

Параметр SQL_CALC_FOUND_ROWS в запросе возвращает количество строк, которые вернул бы оператор SELECT, если бы не был указан LIMIT. Искомое количество строк можно получить при помощи запроса SELECT FOUND_ROWS().

Выполним этот запрос и получим данные (записи из таблицы `adverts`), пока только в виде ресурса.

$result_list mysql_query($sql$db);

А теперь получим в переменную $count количество всех строк с помощью вышеупомянутой функции FOUND_ROWS(). Вот так:

$result_found_rows mysql_query("SELECT FOUND_ROWS() as `count`"$db);
$count mysql_fetch_assoc($result_found_rows);

...и «скормим» количество $count объекту $paginationManager посредством метода setCount():

$paginationManager->setCount($count["count"]);

Всё, на этом наша работа напрямую с объектом $paginationManager заканчивается. Давайте полученные данные из таблицы `adverts` сохраним в переменной $data:

while (($row mysql_fetch_assoc($result_list)) !== false) {
    
$data[] = $row["message"];
}

И подключим HTML-шабон, который мы создадим чуть ниже:

include("template.phtml");

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

<?php
include_once("./Pagination/Manager.php");
include_once(
"./Pagination/Helper.php");

$db mysql_connect("localhost""vasya""password");
mysql_select_db("project"$db);

$paginationManager = new Pagination_Manager(105$_REQUEST);

$sql "SELECT
           SQL_CALC_FOUND_ROWS
           `message`
        FROM
           `adverts`
        ORDER BY
           `id` DESC
        LIMIT " 
.
           
$paginationManager->getStartLimit() . "," .
           
$paginationManager->getStopLimit();

$result_list mysql_query($sql$db);

$result_found_rows mysql_query("SELECT FOUND_ROWS() as `count`"$db);
$count mysql_fetch_assoc($result_found_rows);

$paginationManager->setCount($count["count"]);

while ((
$row mysql_fetch_assoc($result_list)) !== false) {
    
$data[] = $row["message"];
}

include(
"template.phtml");

Как видите, всё очень просто — вся работа объекта класса `Pagination_Manager` в контроллере заключена в этих трёх действиях:

// Инстанцирование объекта
$paginationManager = new Pagination_Manager(105$_REQUEST);

// Подстановка в выражение LIMIT start и stop позиций
$sql "... LIMIT " .
           
$paginationManager->getStartLimit() . "," .
           
$paginationManager->getStopLimit();

// Установка количества строк, которые вернул бы SQL-запрос, если бы LIMIT не был указан
$paginationManager->setCount($count["count"]);

Работа с шаблоном: вывод записей

Создайте файл шаблона `template.html` примерно такого содержания:

<!DOCTYPE html>
<
html>
<
head>
    <
title>Бесплатные объявления</title>
</
head>
<
body>
    <?
php
    $counter 
$paginationManager->getAutoincrementNum();
    
?>
    <?php foreach($data as $message): ?>
        <p><?=($counter++)?><?=htmlspecialchars($message);?></p>
    <?php endforeach; ?>
</body>
</html>

Данный шаблон с помощью цикла foreach выводит записи, которые мы поместили в массив $data в PHP-скрипте, функция htmlspecialchars я надеюсь, вы знаете, что делает. Что делает нижестоящая строка кода:

$counter $paginationManager->getAutoincrementNum();

Метод getAutoincrementNum() объекта возвращает число для обозначения визуального идентификатора записи в порядке вывода, которое в цикле нужно инкрементировать, т.е. увеличивать, что мы и сделали. Взглянем на пример такого результата:

1. работа на дому

2. продам монеты

3. Стальные двери Балашиха

4. Широкий ассортимент детских товаров в магазине Люлька приятно удивит любую маму

5. Сдача офиса

6. Продаются щенки породы чау-чау

7. Чехия на 8 марта

8. РАБОТА И ОБУЧЕНИЕ

9. Продается электропианиноYAMAHA P-85S

10. Продаем фанеру ФК и ФСФ и другие стройматериалы

...и т.д.

— как видно, записи нумеруются с цифры 1 и счетчик увеличивается по мере вывода записей.

Аналогично, существует метод getAutodecrementNum(), полученное значение которого в цикле надо декрементировать, т.е. уменьшать. Давайте попробуем это сделать:

<!DOCTYPE html>
<
html>
<
head>
    <
title>Бесплатные объявления</title>
</
head>
<
body>
    <?
php
    $counter 
$paginationManager->getAutodecrementNum();
    
?>
    <?php foreach($data as $message): ?>
        <p><?=($counter--)?><?=htmlspecialchars($message);?></p>
    <?php endforeach; ?>
</body>
</html>

Взглянем на пример такого результата:

3200. работа на дому

3199. продам монеты

3198. Стальные двери Балашиха

3197. Широкий ассортимент детских товаров в магазине Люлька приятно удивит любую маму

3196. Сдача офиса

3195. Продаются щенки породы чау-чау

3194. Чехия на 8 марта

3193. РАБОТА И ОБУЧЕНИЕ

3192. Продается электропианиноYAMAHA P-85S

3191. Продаем фанеру ФК и ФСФ и другие стройматериалы

...и т.д.

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

Определять, какой вид числовых идентификаторов использовать — решать вам. Обычно, если записи выводятся начиная с новых (ORDER BY `id` DESC), то предпочтительно использовать метод getAutodecrementNum(), если начиная со старых (ORDER BY `id` ASC) — getAutoincrementNum(). А можно вообще не нумеровать записи — в 80% случаев я так и поступаю, т.к. в визуальная нумерация записей нужна далеко не всегда.

Работа с шаблоном: создание интерфейса для перехода между страницами

Интерфейс пагинатора создается с помощью объекта `Pagination_Helper` непосредственно в шаблоне:

<!DOCTYPE html>
<
html>
<
head>
    <
title>Бесплатные объявления</title>
    <
style type="text/css">
    .
normal_link,
    .
active_link{
        
font-size:15px;
        
border:1px solid #ccc;
        
padding:3px;
    }
    .
active_link{
        
border:1px solid red;
    }
    </
style>
</
head>
<
body>
    <?
php
    $counter 
$paginationManager->getAutoincrementNum();
    
?>
    <?php foreach($data as $message): ?>
        <p><?=($counter++)?><?=htmlspecialchars($message);?></p>
    <?php endforeach; ?>

    <?php
    
// Инстанцирование объекта `Pagination_Helper`,
    // в него передаётся объект класса `Pagination_Manager` $paginationManager
    
$paginationHelper = new Pagination_Helper($paginationManager);

    
// Настройка внешнего вида пагинатора
                       // Хотим получить стандартный вид пагинации
    
$paginationHelper->setPaginationType(Pagination_Helper::PAGINATION_NORMAL_TYPE)
                       
// Устанавливаем CSS-класс каждого элемента <a> в интерфейсе пагинатора
                     
->setCssNormalLinkClass("normal_link")
                       
// Устанавливаем CSS-класс элемента <span> в интерфейсе пагинатора,
                       // страница которого открыта в текущий момент.
                     
->setCssActiveLinkClass("active_link")
                       
// Параметр для query string гиперссылки
                     
->setRequestUriParameter("param_1""value_1")
                       
// Параметр для query string гиперссылки
                     
->setRequestUriParameter("param_2""value_2")
                       
// Устанавливаем идентификатор фрагмента гиперссылок пагинатора
                     
->setFragmentIdentifier("result1");
    
?>
    <div>
        Всего записей: <strong><?=$paginationHelper->getPagination()->getCount()?></strong>
        <?php if ($paginationHelper->getPagination()->getCount()): ?>
            <br /><br /><span>Страницы:</span>
            <?=$paginationHelper->getHtml()?>
        <?php endif; ?>
    </div>
</body>
</html>

Взглянем на результат:

1. Обустроенный участок, дом, закрытая беседка, баня, хоз. блок, дровяница.

2. Магазин "ЗАМКИ"- Магазин замков и дверной фурнитуры

3. Кровати одно, двух, трехъярусные; шкафы, комоды, диваны, столы из дерева. Матрасы. Размеры любые.

4. Японская раритетная военная дрель 1939г.

5. Устройство для заряда авиационных акб

6. Насосы конденсатные КС 20-50 ухл-4

7. Кожанную куртку Strellson

8. Готовая франшиза

9. Анапа Витязево частный сектор Гостевой дом Родничок цены без посредников

10. Работа в крупной Российской компании

Всего записей: 31369

Страницы: 1 2 3 4 5  »  »»  »»»

Примеры использования и вариации

Давайте попробуем изменить вид ссылок пагинатора, сменив тип пагинатора на интервальной декрементный:

<!DOCTYPE html>
<
html>
...
<?
php
$paginationHelper
->setPaginationType(Pagination_Helper::PAGINATION_DECREMENT_TYPE)
?>
...
</body>
</html>

Взглянем на результат:

1. Обустроенный участок, дом, закрытая беседка, баня, хоз. блок, дровяница.

2. Магазин "ЗАМКИ"- Магазин замков и дверной фурнитуры

3. Кровати одно, двух, трехъярусные; шкафы, комоды, диваны, столы из дерева. Матрасы. Размеры любые.

4. Японская раритетная военная дрель 1939г.

5. Устройство для заряда авиационных акб

6. Насосы конденсатные КС 20-50 ухл-4

7. Кожанную куртку Strellson

8. Готовая франшиза

9. Анапа Витязево частный сектор Гостевой дом Родничок цены без посредников

10. Работа в крупной Российской компании

Всего записей: 31369

Страницы: 31369 - 31360 31359 - 31350 31349 - 31340 31339 - 31330 31329 - 31320  »  »»  »»»

Попробуем изменить визуальный идентификатор записей с помощью метода getAutodecrementNum(), о котором было сказано выше:

<!DOCTYPE html>
<
html>
...
<?
php
$counter 
$paginationManager->getAutodecrementNum();
?>
<?php 
foreach($data as $message): ?>
    <p><?=($counter--)?><?=htmlspecialchars($message);?></p>
<?php endforeach; ?>
...
</body>
</html>

Взглянем на результат:

31369. Обустроенный участок, дом, закрытая беседка, баня, хоз. блок, дровяница.

31368. Магазин "ЗАМКИ"- Магазин замков и дверной фурнитуры

31367. Кровати одно, двух, трехъярусные; шкафы, комоды, диваны, столы из дерева. Матрасы. Размеры любые.

31366. Японская раритетная военная дрель 1939г.

31365. Устройство для заряда авиационных акб

31364. Насосы конденсатные КС 20-50 ухл-4

31363. Кожанную куртку Strellson

31362. Готовая франшиза

31361. Анапа Витязево частный сектор Гостевой дом Родничок цены без посредников

31360. Работа в крупной Российской компании

Всего записей: 31369

Страницы: 31369 - 31360 31359 - 31350 31349 - 31340 31339 - 31330 31329 - 31320  »  »»  »»»

Создадим что-нибудь оригинальное:

<!DOCTYPE html>
<
html>
...
    <?
                       
// Хотим получить стандартный вид пагинации
    
$paginationHelper->setPaginationType(Pagination_Helper::PAGINATION_NORMAL_TYPE)
                       
// Устанавливаем CSS-класс каждого элемента <a> в интерфейсе пагинатора
                     
->setCssNormalLinkClass("normal_link")
                       
// Устанавливаем CSS-класс элемента <span> в интерфейсе пагинатора,
                       // страница которого открыта в текущий момент.
                     
->setCssActiveLinkClass("active_link")
                       
// Параметр для query string гиперссылки
                     
->setRequestUriParameter("param_1""value_1")
                       
// Параметр для query string гиперссылки
                     
->setRequestUriParameter("param_2""value_2")
                       
// Устанавливаем идентификатор фрагмента гиперссылок пагинатора
                     
->setFragmentIdentifier("result4")
                       
// Не показываем сслыки перехода на первую («««)...
                     
->setViewFirstPageLabel(false)
                       
// ..и последнюю (»»») страницу
                     
->setViewLastPageLabel(false)
                       
// Не показываем ссылки перехода между блоками страниц («« и »»)
                     
->setViewPreviousBlockLabel(false)
                     ->
setViewNextBlockLabel(false)
                       
// Заменим якоря тегов перехода между страницами (« и »)
                     
->setPreviousPageAnchor("&larr; Туда")
                     ->
setNextPageAnchor("Сюда &rarr;");
    
?>
...
</body>
</html>

Взглянем на результат:

31369. Обустроенный участок, дом, закрытая беседка, баня, хоз. блок, дровяница.

31368. Магазин "ЗАМКИ"- Магазин замков и дверной фурнитуры

31367. Кровати одно, двух, трехъярусные; шкафы, комоды, диваны, столы из дерева. Матрасы. Размеры любые.

31366. Японская раритетная военная дрель 1939г.

31365. Устройство для заряда авиационных акб

31364. Насосы конденсатные КС 20-50 ухл-4

31363. Кожанную куртку Strellson

31362. Готовая франшиза

31361. Анапа Витязево частный сектор Гостевой дом Родничок цены без посредников

31360. Работа в крупной Российской компании

Всего записей: 31369

Страницы: 1 2 3 4 5  Сюда →

Скачать код