Скрипт постраничной навигации (Пагинация на PHP)

10:02 13.07.2017
Я не автор данного сценария и темы в целом. Я скопировал тему со своего старого форума.

Иногда, при большом количестве записей на странице, требуется создать постраничную навигацию (пагинацию). Не все пользуются фреймворками, в которых уже реализован модуль навигации. Часто новички для навигации используют старый скрипт с SoftTime, который не очень удобен. Однажды я для случаев, когда у заказчика сайт написан не на фреймворке, а ему нужно встроить навигацию, я создал свой класс навигации. Может кому-нибудь пригодится, выкладываю:
<?php 
    /*
    * Класс для генерации постраничной навигации
    */
    class Pagination
    {
        /**
        * 
        * @var Ссылок навигации на страницу
        * 
        */
        private $max = 10;
        
        /**
        * 
        * @var Ключ для GET, в который пишется номер страницы
        * 
        */
        private $index = 'page';
        
        /**
        * 
        * @var Текущий GET-запрос
        * 
        */
        private $query;
        
        /**
        * 
        * @var Текущая страница
        * 
        */
        private $current_page;
        
        /**
        * 
        * @var Общее количество записей
        * 
        */
        private $total; 
        
        /**
        * 
        * @var Записей на страницу
        * 
        */
        private $limit;
        
        /**
        * Запуск необходимых данных для навигации
        * @param integer $total - общее количество записей
        * @param integer $limit - количество записей на страницу
        * 
        * @return
        */
        public function __construct( $total, $limit )
        {
            # Устанавливаем общее количество записей
            $this->total  = $total;
            
            # Устанавливаем количество записей на страницу
            $this->limit  = $limit;
            
            # Устанавливаем количество страниц
            $this->amount = $this->amount();
            
            # Вызываем метод установки текущей страницы
            $this->setCurrentPage();
            
            # Вызываем метод установки текущего GET-запроса
            $this->setQueryString();
        }
        
        /**
        *  Для вывода ссылок
        * 
        * @return HTML-код со ссылками навигации
        */
        public function get()
        {            
            # Для записи ссылок
            $links = null;
            
            # Получаем ограничения для цикла
            $limits = $this->limits();
            
            # Генерируем ссылки
            for($page=$limits[0]; $page<=$limits[1]; $page++)
            {
                # Если текущая это текущая страница
                if($page == $this->current_page)
                    # Обводим жирным
                    $links .= '<b>'. $page .'</b>';
                else
                    # Заносим ссылку
                    $links .= $this->generateHtml($page);
            }
            
            # Если ссылки создались
            if(!is_null( $links ))
            {
                # Если текущая страница не первая
                if($this->current_page > 1)
                    # Создаём ссылку "На первую"
                    $links = $this->generateHtml(1, '<') . $links;
                
                # Если текущая страница не первая
                if($this->current_page < $this->amount)
                    # Создаём ссылку "На последнюю"
                    $links .= $this->generateHtml($this->amount, '>');                
            }
            
            # Возвращаем ссылки
            return $links;
        }
        
        /**
        * Для генерации HTML-кода ссылки
        * @param string $query - текущий GET-запрос
        * @param integer $page - номер страницы
        * 
        * @return
        */
        private function generateHtml( $page, $text=null ){
            # Если текст ссылки не указан
            if(!$text)
                # Указываем, что текст - цифра страницы
                $text = $page;
                
            # Формируем HTML код ссылки и возвращаем
            return
                '<a href="?'. $this->query .'&'. $this->index .'='. $page .'">'. $text .'</a>';
        }
        
        /**
        *  Для получения, откуда стартовать
        * 
        * @return массив с началом и концом отсчёта
        */
        private function limits()
        {
            # Вычисляем ссылки слева (чтобы активная ссылка была посередине)
            $left = $this->current_page - round($this->max / 2);
 
            # Вычисляем начало отсчёта
            $start = $left > 0 ? $left : 1;                
            
            # Если впереди есть как минимум $this->max страниц
            if($start + $this->max <= $this->amount)
                # Назначаем конец цикла вперёд на $this->max страниц или просто на минимум
                $end = $start > 1 ? $start + $this->max : $this->max;
            else{
                # Конец - общее количество страниц
                $end = $this->amount;

                # Начало - минус $this->max от конца
                $start = $this->amount - $this->max > 0 ? $this->amount - $this->max : 1;
              
            }
            
            # Возвращаем
            return 
                array($start, $end);
        }
 
        /**
        * Для установки текущей страницы
        * 
        * @return
        */
        private function setCurrentPage()
        {
            # Получаем номер страницы
            $this->current_page = isset($_GET[$this->index]) ? (int) $_GET[$this->index] : 1;
            
            # Если текущая страница боле нуля
            if($this->current_page > 0)
            {
                # Если текунщая страница меньше общего количества страниц
                if($this->current_page > $this->amount)
                    # Устанавливаем страницу на последнюю
                    $this->current_page = $this->amount;
            }
            else
                # Устанавливаем страницу на первую
                $this->current_page = 1;
        }
        
        /**
        * Для получения и установки текущего GET-запроса
        * 
        * @return
        */
        private function setQueryString(){
            # Получаем параметры текущего запроса
            $query = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY );
            
            # Разбираем строку запроса
            parse_str( $query, $params );
            
            # Удаляем значение страницы, если есть
            unset( $params[$this->index] );
            
            # Формируем запрос
            $this->query = http_build_query( $params );
        }
        
        /**
        * Для получеия общего числа страниц
        * 
        * @return число страниц
        */
        private function amount()
        {
            # Делим и возвращаем
            return
                round( $this->total / $this->limit );
        }
    }

Поместите этот код в отдельный файл и подключите к своему скрипту. Использовать так:
/**
    * Начинаем работу с постраничной навигацией
    * Первый аргумент - общее количество
    * Второй - количество записей на страницу
    */
    $pagination = new Pagination(100, 5);
    
    # Выводим навигацию
    echo $pagination->get();

Как видите, всё очень просто. На страницу выведутся все ссылки навигации. Вам остаётся дописать свой скрипт так, чтобы данные из базы вытягивались соответственно переданной странице.
10:06 13.07.2017
Я поработал над этим классом, и добавил в него два полезнейших метода: skip и take. Теперь пользоваться классом ещё удобнее! Класс всё сделает за Вас, больше не нужно для получения данных, которые нужно разбить на страницы, из переменной GET получать значение page.
Достаточно только указать общее количество записей и указать, сколько записей на страницу показывать.
<?php 
    /*
    * Класс для генерации постраничной навигации
    */
    class Pagination
    {
        /**
        * 
        * @var Ссылок навигации на страницу
        * 
        */
        private $max = 10;
        
        /**
        * 
        * @var Ключ для GET, в который пишется номер страницы
        * 
        */
        private $index = 'page';
        
        /**
        * 
        * @var Текущий GET-запрос
        * 
        */
        private $query;
        
        /**
        * 
        * @var Текущая страница
        * 
        */
        private $current_page;
        
        /**
        * 
        * @var Общее количество записей
        * 
        */
        private $total; 
        
        /**
        * 
        * @var Записей на страницу
        * 
        */
        private $limit;
        
        /**
        * Запуск необходимых данных для навигации
        * @param integer $total - общее количество записей
        * @param integer $limit - количество записей на страницу
        * 
        * @return
        */
        public function __construct( $total, $limit )
        {
            # Устанавливаем общее количество записей
            $this->total  = $total;
            
            # Устанавливаем количество записей на страницу
            $this->limit  = $limit;
            
            # Устанавливаем количество страниц
            $this->amount = $this->amount();
            
            # Вызываем метод установки текущей страницы
            $this->setCurrentPage();
            
            # Вызываем метод установки текущего GET-запроса
            $this->setQueryString();
        }
        
        /**
        *  Для вывода ссылок
        * 
        * @return HTML-код со ссылками навигации
        */
        public function get()
        {
            # Для записи ссылок
            $links = null;
            
            # Получаем ограничения для цикла
            $limits = $this->limits();
            
            # Генерируем ссылки
            for($page=$limits[0]; $page<=$limits[1]; $page++)
            {
                # Если текущая это текущая страница
                if($page == $this->current_page)
                    # Обводим жирным
                    $links .= '<b>'. $page .'</b>';
                else
                    # Заносим ссылку
                    $links .= $this->generateHtml($page);
            }
            
            # Если ссылки создались - генерируем "Следующая", "Предыдущая", "Первая", "Последняя"
            if(!is_null( $links ))
            {
                # Если текущая страница не первая
                if($this->current_page > 1){
                    # Создаём ссылку "Предыдущая"
                    $links = $this->generateHtml($this->current_page - 1, '<', 'Предыдущая') . $links;
                    
                    # Создаём ссылку "Первая"
                    $links = $this->generateHtml(1, '<<', 'Первая') . $links;
                }
                
                # Если текущая страница не первая
                if($this->current_page < $this->amount){
                    # Создаём ссылку "Следующая"
                    $links .= $this->generateHtml($this->current_page + 1, '>', 'Следующая');  
                    
                    # Создаём ссылку "Следующая"
                    $links .= $this->generateHtml($this->amount, '>>', 'Последняя');  
                }   
            }
            
            # Возвращаем ссылки
            return $links;
        }
        
        /**
        * Для получения, откуда начинать выборку
        * 
        * @return integer
        */
        public function skip(){
            return 
                $this->current_page * $this->limit - $this->limit;
        }
        
        /**
        * Для получение ограничения выборки
        * 
        * @return integer
        */
        public function take(){
            # Получаем, откуда начинаем
            $skip = $this->skip();
            
            # Возвращаем ограницение
            return
                $skip + $this->limit > $this->total ? $this->total - $skip : $this->limit;
        }
        
        /**
        * Для генерации HTML-кода ссылки
        * @param string $query - текущий GET-запрос
        * @param integer $page - номер страницы
        * 
        * @return
        */
        private function generateHtml( $page, $text=null, $title=null ){
            # Если текст ссылки не указан
            if(!$text)
                # Указываем, что текст - цифра страницы
                $text = $page;
            
            # Формируем ссылку
            $query = $this->index .'='. $page;
            
            # Формируем строку запроса (после вопроса)
            $query = $this->query ? $this->query .'&'. $query : $query;
                
            # Формируем HTML код ссылки и возвращаем
            return
                '<a href="?'. $query .'" title="'. $title .'">'. $text .'</a>';
        }
        
        /**
        *  Для получения, откуда стартовать вывод ссылок
        * 
        * @return массив с началом и концом отсчёта
        */
        private function limits()
        {
            # Вычисляем ссылки слева (чтобы активная ссылка была посередине)
            $left = $this->current_page - round($this->max / 2);
 
            # Вычисляем начало отсчёта
            $start = $left > 0 ? $left : 1;                
            
            # Если впереди есть как минимум $this->max страниц
            if($start + $this->max <= $this->amount)
                # Назначаем конец цикла вперёд на $this->max страниц или просто на минимум
                $end = $start > 1 ? $start + $this->max : $this->max;
            else{
                # Конец - общее количество страниц
                $end = $this->amount;
 
                # Начало - минус $this->max от конца
                $start = $this->amount - $this->max > 0 ? $this->amount - $this->max : 1;
              
            }
            
            # Возвращаем
            return [$start, $end];
        }
 
        /**
        * Для установки текущей страницы
        * 
        * @return
        */
        private function setCurrentPage()
        {
            # Получаем номер страницы
            $this->current_page = isset($_GET[$this->index]) ? (int) $_GET[$this->index] : 1;
            
            # Если текущая страница боле нуля
            if($this->current_page > 0)
            {
                # Если текунщая страница меньше общего количества страниц
                if($this->current_page > $this->amount)
                    # Устанавливаем страницу на последнюю
                    $this->current_page = $this->amount;
            }
            else
                # Устанавливаем страницу на первую
                $this->current_page = 1;
        }
        
        /**
        * Для получения и установки текущего GET-запроса
        * 
        * @return
        */
        private function setQueryString(){
            # Получаем параметры текущего запроса
            $query = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY );
            
            # Разбираем строку запроса
            parse_str( $query, $params );
            
            # Удаляем значение страницы, если есть
            unset( $params[$this->index] );
            
            # Формируем запрос
            $this->query = http_build_query( $params );
        }
        
        /**
        * Для получеия общего числа страниц
        * 
        * @return число страниц
        */
        private function amount()
        {
            # Делим и возвращаем
            return
                ceil( $this->total / $this->limit );
        }
    }

Пример использования:
<?php
    # Подключаем класс постраничной навигации
    require_once('pagination.php');
    
    # Получаем общее количество записей в таблице
    $query  = mysqli_query($db, 'SELECT COUNT(*) AS `count` FROM `articles`');  
    $result = mysqli_fetch_assoc($query);

    # Получаем объект класса постраничной навигации
    $pagination = new Pagination($result['count'], 15);
    
    # Получаем из базы данных записи для этой страницы
    $query  = mysqli_query($db, 'SELECT * FROM `articles` LIMIT ' . $pagination->skip() .', '. $pagination->take());  
    
    while($result = mysqli_fetch_assoc($query)){
        # Здесь можно вывести данные или сформировать массив для шаблона
    }
    
    # Выводим постраничную навигацию
    echo $pagination->get();

Вот и всё! Никаких манипуляций с $_GET['page']. Мы указали для ограничения выборки $pagination->skip() и $pagination->take().
Согласитесь, очень удобно.
Скажу также, что этот класс постраничной навигации сохраняет все значения, которые на данный момент присутствуют в URL. То, есть, у нас имеется такой URL:
site.ru/articles?order=desc
Этот класс для навигации выдаст нам такой URL:
site.ru/articles?order=desc&page=2
То есть просто добавит номер страницы к текущему URL и никакие значения из предыдущего запроса не будут утеряны!

Если у кого-то возникнут вопросы по стилизации (наведении красоты) постраничной навигации, привожу пример:
<?php
    # Подключаем класс постраничной навигации
    require_once('pagination.php');
    
    # Получаем объект класса постраничной навигации
    $pagination = new Pagination(100, 15);
?>

<style>
    /**
    * Стили для постраничной навигации, поместите их в Ваш CSS-файл
    */
    .pagination a{
        background: #EEE;
        color: #333;
        transition: ease-out 0.5s;
    }
    .pagination b,
    .pagination a:hover{
        background: #F6F6F6;
        color: #999;
    }
    .pagination a:hover{
        transition: ease-out 0.5s;
    }
    .pagination a,
    .pagination b{
        border: 1px solid #DFDFDF;
        border-radius: 5px;
        display: inline-block;
        font: bold 12px Arial;
        line-height: 5px;
        margin: 2px;
        padding: 10px;
        text-align: center;
        text-decoration: none;
}
</style>

<div class="pagination">
    <?=$pagination->get()?>
</div>
10:07 13.07.2017
И снова здравствуйте! :)
Немного доработал класс постраничной навигации. Теперь он совместим с Twitter bootstrap. Т.е. если Вы используйте bootstrap, то Вам не нужно будет заботиться о стилизации навигации. К ней применятся стили, которые изначально реализованы в bootstrap!
<?php 
    /*
    * Класс для генерации постраничной навигации
    */
    class Pagination
    {
        /**
        * 
        * @var Активных ссылок навигации на страницу.
        * Т.е. ссылок, помимо текущей (активной) ссылки и ссылок влево-вправо.
        * 
        */
        private $max = 6;
        
        /**
        * 
        * @var Ключ для GET, в который пишется номер страницы
        * 
        */
        private $index = 'page';
        
        /**
        * 
        * @var Текущий GET-запрос
        * 
        */
        private $query;
        
        /**
        * 
        * @var Текущая страница
        * 
        */
        private $current_page;
        
        /**
        * 
        * @var Общее количество записей
        * 
        */
        private $total; 
        
        /**
        * 
        * @var Записей на страницу
        * 
        */
        private $limit;
        
        /**
        * Запуск необходимых данных для навигации
        * @param integer $total - общее количество записей
        * @param integer $limit - количество записей на страницу
        * 
        * @return
        */
        public function __construct( $total, $limit )
        {
            # Устанавливаем общее количество записей
            $this->total  = $total;
            
            # Устанавливаем количество записей на страницу
            $this->limit  = $limit;
            
            # Устанавливаем количество страниц
            $this->amount = $this->amount();
            
            # Вызываем метод установки текущей страницы
            $this->setCurrentPage();
            
            # Вызываем метод установки текущего GET-запроса
            $this->setQueryString();
        }
        
        /**
        *  Для вывода ссылок
        * 
        * @return HTML-код со ссылками навигации
        */
        public function get()
        {
            # Для записи ссылок
            $pagination = null;
            
            # Получаем ограничения для цикла
            $limits = $this->limits();

            # Генерируем ссылки
            for($page=$limits[0]; $page<=$limits[1]; $page++)
            {
                # Формируем статус ссылки
                $status = $page == $this->current_page ? 'active' : null;
                    
                # Заносим ссылку
                $pagination .= $this->generateHtml($page, null, null, $status);
            }
            
            # Если текущая страница не первая
            if($this->current_page > 1){
                # Создаём ссылку "Предыдущая"
                $pagination = $this->generateHtml($this->current_page - 1, '<', 'Предыдущая') . $pagination;
                
                # Создаём ссылку "Первая"
                $pagination = $this->generateHtml(1, '<<', 'Первая') . $pagination;
            }
            
            # Если текущая страница не первая
            if($this->current_page < $this->amount){
                # Создаём ссылку "Следующая"
                $pagination .= $this->generateHtml($this->current_page + 1, '>', 'Следующая');  
                
                # Создаём ссылку "Следующая"
                $pagination .= $this->generateHtml($this->amount, '>>', 'Последняя');  
            }
            
            # Оборачиваем ссылки
            $pagination = '<ul class="pagination">'. $pagination .'</ul>';

            # Возвращаем ссылки
            return $pagination;
        }
        
        /**
        * Для получения, откуда начинать выборку
        * 
        * @return integer
        */
        public function skip(){
            return 
                $this->current_page * $this->limit - $this->limit;
        }
        
        /**
        * Для получение ограничения выборки
        * 
        * @return integer
        */
        public function take(){
            # Получаем, откуда начинаем
            $skip = $this->skip();
            
            # Возвращаем ограницение
            return
                $skip + $this->limit > $this->total ? $this->total - $skip : $this->limit;
        }
        
        /**
        * Для генерации HTML-кода ссылки
        * @param string $query - текущий GET-запрос
        * @param integer $page - номер страницы
        * 
        * @return
        */
        private function generateHtml( $page, $text=null, $title=null, $status=null ){
            # Если текст ссылки не указан
            if( is_null($text) )
                # Указываем, что текст - цифра страницы
                $text = $page;
            
            # Формируем ссылку
            $query = $this->index .'='. $page;
            
            # Формируем строку запроса (после вопроса)
            $query = $this->query ? $this->query .'&'. $query : $query;
            
            # Формируем статус ссылки
            $status = $status ? 'class="'. $status .'"' : null;
                
            # Формируем HTML код ссылки и возвращаем
            return 
                '<li '. $status .'><a href="?'. $query .'" title="'. $title .'">'. $text .'</a></li>';
        }
        
        /**
        *  Для получения, откуда стартовать вывод ссылок
        * 
        * @return массив с началом и концом отсчёта
        */
        private function limits()
        {
            # Вычисляем ссылки слева (чтобы активная ссылка была посередине)
            $left = $this->current_page - round($this->max / 2, 0, PHP_ROUND_HALF_DOWN);

            # Вычисляем начало отсчёта
            $start = $left > 0 ? $left : 1;               

            # Если впереди есть как минимум $this->max страниц
            if($start + $this->max <= $this->amount)
                # Назначаем конец цикла вперёд на $this->max страниц или просто на минимум
                $end = $start >= 1 ? $start + $this->max : $this->max;
            else{
                # Конец - общее количество страниц
                $end = $this->amount;
 
                # Начало - минус $this->max от конца
                $start = $this->amount - $this->max > 0 ? $this->amount - $this->max : 1;
            }
            
            # Возвращаем
            return [$start, $end];
        }
 
        /**
        * Для установки текущей страницы
        * 
        * @return
        */
        private function setCurrentPage()
        {
            # Получаем номер страницы
            $this->current_page = isset($_GET[$this->index]) ? (int) $_GET[$this->index] : 1;
            
            # Если текущая страница боле нуля
            if($this->current_page > 0)
            {
                # Если текунщая страница меньше общего количества страниц
                if($this->current_page > $this->amount)
                    # Устанавливаем страницу на последнюю
                    $this->current_page = $this->amount;
            }
            else
                # Устанавливаем страницу на первую
                $this->current_page = 1;
        }
        
        /**
        * Для получения и установки текущего GET-запроса
        * 
        * @return
        */
        private function setQueryString(){
            # Получаем параметры текущего запроса
            $query = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY );
            
            # Разбираем строку запроса
            parse_str( $query, $params );
            
            # Удаляем значение страницы, если есть
            unset( $params[$this->index] );
            
            # Формируем запрос
            $this->query = http_build_query( $params );
        }
        
        /**
        * Для получеия общего числа страниц
        * 
        * @return число страниц
        */
        private function amount()
        {
            # Делим и возвращаем
            return
                ceil( $this->total / $this->limit );
        }
    }