Простая пагинация на PHP/Javascript. Создаем постраничную навигацию

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

1. Выгружать все данные и показывать пользователю только часть.
2. Выгружать только часть данных, которые непосредственно показывать.

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

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

1. Функция pagePrint(), печатает ссылку на заданую страницу

function pagePrint($page, $title, $show, $active_class = '') {
    if($show) {
        echo '<a href="?do=list&page=' . $page . '">' . $title . '</a>';
    } else {
        if(!empty($active_class)) $active = 'class="' . $active_class . '"';
        echo '<span ' . $active . '>' . $title . '</span>';
    }
    return false;
}

$page – номер страницы;
$title – анкор ссылки (например, “1”, “2”, “10”, “следующая”);
$show – показывать ссылку или текст, используется, чтобы вывести текущую страницу, или же неактивные ссылки “назад”, “вперед”;
$active_class – класс CSS для активной страницы.

2. Начальный конфиг

$page_setting = [
    'limit' => 50, // кол-во записей на странице
    'show'  => 5, // 5 до текущей и после
    'prev_show' => 0, // не показывать кнопку "предыдущая"
    'next_show' => 0, // не показывать кнопку "следующая"
    'first_show' => 0, // не показывать ссылку на первую страницу
    'last_show' => 0, // не показывать ссылку на последнюю страницу
    'prev_text' => 'назад',
    'next_text' => 'вперед',
    'class_active' => 'active',
    'separator' => ' ... ',
];
$page = (int) $_GET['page'];
if(empty($page)) $page = 1; // если страница не задана, показываем первую

3. Пример выборки данных из базы

$start = ($page-1)*$page_setting['limit'];
$res = $db->query("SELECT * FROM table LIMIT {$start},{$page_setting['limit']}");

Для выборки используется LIMIT, где старт вычисляется по формуле (page-1)*limit, то есть для первой страницы start = 0.

4. Подсчет кол-ва страниц и проверка основных условий.

С подсчетом количества страниц иногда возникают проблемы, так как страниц может быть только целое число, а при делении (кол-во записей)/(лимит записей на странице) может получится дробное. Данный результат необходимо округлить в больную строну, потому что, обычное округление round(), выдаст ошибочный результат.

Пример, всего записей 31, на странице публикуем по 10, таким образом по формуле получается 3.1 страница, при округлении round(3.1) = 3, что неправильно, так как теряется одна запись. Поэтому используется функция ceil(), ceil(3.1) = 4.

$res = $db->query("SELECT count(*) AS count FROM dle_getmovie_movies {$where}");
$row = $res->fetch(PDO::FETCH_ASSOC);
$page_count = ceil($row['count'] / $page_setting['limit']); // кол-во страниц
$page_left = $page - $page_setting['show']; // находим левую границу
$page_right = $page + $page_setting['show']; // находим правую границу
$page_prev = $page - 1; // узнаем номер предыдушей страницы
$page_next = $page + 1; // узнаем номер следующей страницы
if($page_left < 2) $page_left = 2; // левая граница не может быть меньше 2, так как 2 - первое целое число после 1
if($page_right > ($page_count - 1)) $page_right = $page_count - 1; // правая граница не может ровняться или быть больше, чем всего страниц
if($page > 1) $page_setting['prev_show'] = 1; // если текущая страница не первая, значит существует предыдущая
if($page != 1) $page_setting['first_show'] = 1; // показываем ссылку на первую страницу, если мы не на ней
if($page < $page_count) $page_setting['next_show'] = 1; // если текущая страница не последняя, значит существуюет следующая
if($page != $page_count) $page_setting['last_show'] = 1;

5. Выводим на экран:

pagePrint($page_prev, $page_setting['prev_text'], $page_setting['prev_show']);
pagePrint(1, 1, $page_setting['first_show'], $page_setting['class_active']);
if($page_left > 2) echo $page_setting['separator'];
for($i = $page_left; $i <= $page_right; $i++) {
    $page_show = 1;
    if($page == $i) $page_show = 0;
    pagePrint($i, $i, $page_show, $page_setting['class_active']);
}
if($page_right < ($page_count - 1)) echo $page_setting['separator'];
if($page_count != 1) pagePrint($page_count, $page_count, $page_setting['last_show'], $page_setting['class_active']);
pagePrint($page_next, $page_setting['next_text'], $page_setting['next_show']);

Вот и все, алгоритм разобран, ниже представлен код для вывода пагинации на Javascript:

// функция смены страницы
function pageChange(page) {
    var page_show = 0;
    var page_txt = '';
    var setting = page_setting();
    setting.page_left = page - setting.show; // находим левую границу
    setting.page_right = page + setting.show; // находим правую границу
    setting.page_prev = page - 1; // узнаем номер предыдушей страницы
    setting.page_next = page + 1; // узнаем номер следующей страницы
    if(setting.page_left < 2) setting.page_left = 2; // левая граница не может быть меньше 2, так как 2 - первое целое число после 1
    if(setting.page_right > (setting.page_count - 1)) setting.page_right = setting.page_count - 1; // правая граница не может ровняться или быть больше, чем всего страниц
    if(page > 1) setting.prev_show = 1; // если текущая страница не первая, значит существует предыдущая
    if(page != 1) setting.first_show = 1; // показываем ссылку на первую страницу, если мы не на ней
    if(page < setting.page_count) setting.next_show = 1; // если текущая страница не последняя, значит существуюет следующая
    if(page != setting.page_count) setting.last_show = 1;

    page_txt += pagePrint(setting.page_prev, setting.prev_text, setting.prev_show);
    page_txt += pagePrint(1, 1, setting.first_show, setting.class_active);
    if(setting.page_left > 2) page_txt += setting.separator;
    for(var i = setting.page_left; i <= setting.page_right; i++) {
        page_show = 1;
        if(page == i) page_show = 0;
        page_txt += pagePrint(i, i, page_show, setting.class_active);
    }
    if(setting.page_right < (setting.page_count - 1)) page_txt += setting.separator;
    if(setting.page_count != 1) page_txt += pagePrint(setting.page_count, setting.page_count, setting.last_show, setting.class_active);
    page_txt += pagePrint(setting.page_next, setting.next_text, setting.next_show);
    document.getElementById("pagenavi").innerHTML = page_txt;

    return false;
}

// функция для публикации ссылки на страницу
function pagePrint(page, title, show, active_class = '') {
    if(show) {
        return '<a href="javascript:return false;" onclick="pageChange(' + page + ')">' + title + '</a>';
    } else {
        var active = '';
        if(active_class != '') active = 'class="' + active_class + '"';
        return '<span ' + active + '>' + title + '</span>';
    }
    return false;
}


var page = 1; // получаем номер страницы, для примера 1
if(page != undefined) page = 1; // если страница не задана, показываем первую
var page_count = 10; // считаем кол-во страниц, для примера 10

// начальные настройки
var page_setting = function() {
    return {
        'limit': 50, // кол-во записей на странице
        'show': 5, // 5 до текущей и после
        'prev_show': 0, // не показывать кнопку "предыдущая"
        'next_show': 0, // не показывать кнопку "следующая"
        'first_show': 0, // не показывать ссылку на первую страницу
        'last_show': 0, // не показывать ссылку на последнюю страницу
        'prev_text': 'назад',
        'next_text': 'вперед',
        'class_active': 'active',
        'separator': ' ... ',
        'page_count': page_count,
        'page_left': 0,
        'page_right': 0,
        'page_prev': 0,
        'page_next': 0
    }
};

pageChange(page); // совершаем первый вызов

Остались вопросы – задаем в комментариях, также делимся своими наработками, если знаете как “красивее” реализовать пагинацию.

Может быть полезно