Как сделать пагинацию php
Простая пагинация на php
Решил описать такой на первый взгляд простой но очень интересный момент в web программирование как пагинация.
Наверное каждый web программист хоть раз да и сталкивался с пагинацией, и понимает что универсального решения нет! По этому я предлагаю посмотреть те простые решения которые сам использую.
p.s. В топике приведен пример пагинации на php, без использования AXAJ и тд. Это сделано для того что б продемонстрировать саму суть, так сказать сделать каркас.
Поехали!
И так начнем. Представим что у нас есть большое количество какого то контента контента (заметки, товары и тд). И вот в один момент мы понимаем что отображать все это на одной странице, неправильно и не хорошо. По этому мы решаем этот контент разбить на части (страницы).
Но для того что б нам это сделать нам нужно знать несколько значений.
Если честно то данных нам нужно немножко больше, но все это зависит от того какую именно пагинацию мы хотим сделать, так что об остальных данных мы поговорим чуть позже.
/*всего страниц или конечная страница*/
/*левый и правый лимиты*/
$iLeftLimit = 4;
$iRightLimit = 5;
Ну вот все данные у нас уже есть и можно заняться самой функцией пагинации.
Давай посмотрим код поближе.
Первый вариант развития событий, это если наша страница находиться где то в центре (например страница 8), и количество страниц с лева и с права могут свободно отображаться, это самый простой вариант из возможных.
А что же будет если мы находиться например на странице номер 2?
Ведь в условии мы задали что нужно отображать с лева 4 ссылки.
Тогда нам на помощь приходит следующий код:
$iSlice — это количество элементов которые не будут показаны с лева, и которые соотвецтвенно нужно добавить справа, что количество ссылок оставалось постоянным.
Можно было б if’ом запретить вывод страницы если она меньше 1 и больше макс. странице но это б было так не красиво.
Pagination на PHP
Чтобы создать Pagination на PHP, нам необходимо иметь на входе несколько параметров:
Теперь мы можем создавать Pagination, вот так это выглядит на PHP:
Сразу скажу, что кода хоть и немного, но он достаточно сложный для новичков. Года 3 назад я потратил очень много времени, чтобы сделать нечто подобное и тогда кода получилось раза в 2 больше.
А дальше можно этот Pagination через CSS украшать так, как Вы пожелаете. Можно и вёрстку изменить при желании (например, вместо угловых скобок “Предыдущая страница” и аналогичных поставить красивые картинки), главное, не трогать PHP-код.
Вот такой весьма удобный Pagination реализован на этом сайте, а так же на некоторых моих других сайтах.
Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!
Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.
Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления
Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.
Порекомендуйте эту статью друзьям:
Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):
Комментарии ( 25 ):
а скрипт вообше рабочий? нажимал на разные ссылки активной остатся 15 сераница
А к БД подключался??
в первом предложении по ссылке пройди.
про контент понятно. не понятно то что при нажатии на ссылку на другую страницу например: 10 стр. Почему активной остается 15 стр.
Спасибо огромное за данную статью! Долго время хотел сделать себе хороший Pagination. Собственный занимал в 5 раз больше места и был неудобен.
Здраствуйте.Я сделал постраничную навигацию, но у меня ошибки появляется Warning: Missing argument 3 for select_data(), как это исправить.
Абай Калдыбаев, в службу поддержки пишите.
Все верно, Александр. В active попадает наша активная страница на данный момент. Каким способом ее вытаскивать, решать Вам.
Делаем пагинацию на PHP
Зачастую в тестовых заданиях на джунов присутствует пункт о реализации пагинации в какой-нибудь системе. И мне довольно часто прилетают просьбы показать как это делается, так как зачастую это задание ставит в тупик начинающих разработчиков. Что-ж, давайте разбираться.
Делать пагинацию я хочу предложить на базе написанного нами в курсе ООП в PHP движка для блога. Готовый проект можно взять по ссылке. Накатите дамп базы и настройте веб-сервер так, чтобы корневой директорией для домена myproject.loc была папка www.
Теперь давайте добавим побольше статей в базу, чтобы было по чему пагинацию делать.
Посмотрим на имеющиеся у нас статьи. Отсортируем их в обратном порядке.
Теперь, если вы еще не в курсе, нужно познакомиться с двумя конструкциями языка SQL: LIMIT и OFFSET. Они позволяют получить только часть строк из тех, что были получены запросом. LIMIT задаёт лимит записей, OFFSET задает количество строк, которые нужно пропустить.
Например, мы хотим пропустить первые 5 строк, и вывести следующие 10 строк:
Как видим, у нас пропустились первые 5 строк с id: 22,21,20,19,18, которые присутствовали в результате запроса без LIMIT и OFFSET. Также мы видим, что строк у нас здесь 10.
Как нетрудно догадаться, для реализации пагинации нам можно воспользоваться этими конструкциями. Если мы хотим выводить на каждой странице нашего блога по 5 записей, то для получения записей на первой странице стоит использовать запрос:
Для второй страницы запрос будет следующим:
То есть здесь мы пропустили первые 5 записей и вывели следующие 5.
Аналогично строим запрос для третьей страницы:
И так далее, пока записи не закончатся.
Получаем формулу для получения запроса, которые выводит записи на n-ой странице блога, где k-число записей на одной странице:
Чтобы понять, сколько у нас будет страниц, нам нужно число записей разделить на число записей, выводимых на одной странице. Полученное число округляем в большую сторону. К примеру, если у нас 22 записи, и на одной странице выводим 5, то число страниц будет 22/5 = 4,4. Округляем в большую сторону и получаем 5. На первых четырех страницах у нас будет по 5 записей, а на последней будет 2 записи.
Получить общее число записей можно с помощью запроса:
Осталось всё это дело закодить. Начнём с самого простого – выведем ссылки на странички со статьями.
В классе ActiveRecordEntity добавляем метод для получения количества страниц. Метод будет принимать на вход количество записей на одной странице.
В MainController вызовем этот метод и передадим в шаблон число страниц.
Добавим в шаблоне внизу странички номера страниц.
Обратите внимание: при формировании ссылки на страницу мы проверяем, является ли страничка первой, и если это так, то формируем ссылку на главную страницу, иначе формируем ссылку вида: /n, где n – номер страницы.
Посмотрим на результат:
Теперь давайте напишем метод для получения записей на n-ой страничке. Для этого в классе ActiveRecordEntity добавим метод getPage(), принимающий на вход количество записей на одной странице и номер страницы.
Добавим в MainController экшен для странички.
Переписываем экшен main.
Проверяем главную страницу:
Добавляем роут для страничек с номерами.
Всё. Пагинация готова. Можно переходить по страничкам. Так выглядит пятая страница.
Из улучшений можно сделать текущий номер страницы не ссылкой, а просто текстом. Для этого передадим номер текущей странички в шаблон.
В шаблоне будем сравнивать текущий номер страницы с тем, который в текущей итерации.
Вот такая простая пагинация у нас получилась. Она подойдет для большинства проектов без высокой нагрузки и с небольшим количеством записей. А для крупных проектов потребуется более специфичное решение, которое я рассмотрю в одном из ближайших уроков.
Изменения, сделанные в ходе урока, вы можете посмотреть на гитхабе.
Постраничная навигация на PHP
Часто при разработке и выводе контента появляется необходимость использования постраничной навигации. Кто-то скорее всего использует готовые решения от своего фреймворка. Кто-то, возможно, не заморачивается и лупит страницы просто циклом. У кого-то есть свои наработки в этом направлении. Вот я как раз и хочу поделиться своим решением данной задачи.
Существует множество вариаций расположения и отображения кнопок, лично я пришел к следующему решению, которое по моему мнению наиболее наглядно и удобно. Подходит как для 5 страниц так и для 5000.
Пример HTML кода
Не буду ходить вокруг да около, сразу приложу пример сформированного скриптом html кода:
Логика построения
По настройкам параметров я остановлюсь чуть позже после приведения кода, сейчас опишу логику формирования самих номеров.
С кнопками «Назад» и «Вперед» думаю все понятно, к тому же их можно просто отключить, поэтому на них не буду заострять внимания.
Первый и последний номер страницы отображается всегда, своего рода кнопки «В начало» и «В конец».
Середина формируется уже по простому алгоритму. Отображается просматриваемая страница и по N страниц по бокам. На примере отображается по N=3 страницы. В принципе все просто и понятно, но особая хитрость используется при приближении к краям. Опишу на примерах:
Страница 1-3 (где 3 = N)
Отображаются первые N*2 страниц и последняя.
Отображается первая и дальше сформированная строка от 4-3=1 до 4+3=7. Первая страница зарезервирована поэтому формируются номера от 2 до 7.
Пожалуй во всех навигациях что я видел (включая хабр) строка была бы сформирована с пропуском, т.е. 1… 3 4 5 6 7 8 9… 17
Но ведь это не логично, отображать многоточие вместо одного числа. При построении второго многоточия выполняется аналогичная проверка.
Середина уже стандартна.
Формирование окончания аналогично началу
Редиректы
Помимо этого из особенностей хочу выделить еще 2 момента, это проверка существования страницы и редирект на «правильный» адрес. Т.е. к примеру, тут же на хабре первая страница может быть доступна сразу по 2м адресам:
habrahabr.ru/sandbox/page1
habrahabr.ru/sandbox
Скрипт не дает зайти на адрес page/1/ и выполняет редирект на «чистый» адрес
Так же если указан слишком большой номер страницы будет выполнен редирект на последнюю существующую. К примеру были удалены материалы или изменено количество записей на страницу. Не могу правда однозначно сказать полезно ли это будет с точки зрения СЕО, но для пользователей мне кажется так будет удобнее.
PHP код и его использование
Для наглядности, приведу пример построения навигации песочницы:
habrahabr.ru/sandbox/page12
Или же если номер страницы прописан внутри URL:
example.com/some_url/1.html — первая страница
example.com/some_url/1-page2.html — вторая страница
$limit — количество записей на страницу
$count_all — общее количество записей
$page_num — номер страницы на которой находится пользователь
На этом, пожалуй, всё. Буду рад любой конструктивной критике.
PS. Огромное спасибо всем отписавшимся, особенно тем кто ругает (и правильно делает).
Обещаю со всем ознакомиться, принять во внимание и исправиться.
Создаем кэшируемую пагинацию, которая не боится неожиданного добавления данных в БД
Если на вашем сайте присутствует большое количество контента, то для отображения пользователю его приходится так или иначе делить.
Все известные мне способы имеют недостатки и я попытался создать систему, которая сможет решить некоторые из них и при этом не будет слишком сложна для реализации.
Существующие методы
1. Пагинация (разделение на отдельные страницы)
Пагинация или разделение на отдельные страницы — достаточно старый способ разделения контента, который, в том числе используется на Хабре. Основным преимуществом является его универсальность и простота реализации как со стороны сервера так и клиентской части.
Код запроса данных из бд чаще всего ограничивается парой строк.
Тут и далее примеры на языке arangodb aql, я скрыл код сервера т.к там пока ничего интересного.
На стороне клиента мы запрашиваем и выводим получившийся результат, я использую vuejs с nuxtjs для примера, но то же самое можно проделать на любом другом стеке, все специфичные для vue моменты я буду подписывать.
Теперь у нас выводятся все посты на странице, но погодите, как пользователи будут переключаться между страницами? Добавим пару кнопок для перелистывания страниц.
Минусы данного способа
При достижении конца страницы пользователю нужно переключаться на следующую страницу вручную.
Не получится кешировать результаты, т.к посты находящиеся на странице 2, при добавлении новых, непременно сместятся на страницу 3, 4 и так далее, т.е одна и та же операция GET возвращает разные результаты в зависимости от количества постов.
Если в момент перелистывания добавятся новые посты, то мы повторно увидим просмотренные элементы на следующей странице и напротив, пропустим если будем листать в обратную сторону.
2. Бесконечный скроллинг
Этот способ решает первую проблему, теперь пользователю не нужно вручную переключаться между страницами.
Основная идея заключается в том, что мы получаем следующую страницу при достижении конца прошлой и добавляем новые элементы к существующим.
При таком подходе проблема №3 проявляются еще более явно, если раньше мы не могли увидеть 2 похожих элемента рядом, то теперь это станет обычной ситуацией, конечно можно воспользоваться грязным трюком и отфильтровывать элементы с совпадающим id прямо на клиенте, но что если добавится 40 новых элементов за раз? Мы потратим 3 запроса к серверу, чтобы достичь новых результатов, т.к прошлые сместятся на 2 страницы (при условии что на одной странице 20 элементов). Это не мой подход!
Как решают эту проблему люди из интернета:
Используют описанный мной выше подход, я не искал подтверждение, но я практически уверен в этом, т.к это самое простое решение которое может прийти на ум, его можно использовать для быстрого прототипирования или создания mvp.
Создают уникальный идентификатор при первом запросе, и сохраняют результаты запроса на сервере, а затем выдают порционно. Тут сразу напрашивается 2 минуса. Во-первых, это использование лишней памяти сервера для хранения результатов всех пользователей. Во-вторых, более сложная реализация, требующая и логики хранения результатов для каждого пользователя, и логики удаления устаревших запросов. Я уверен, что такие реализации существуют и возможно некоторым удалось решить проблему излишней памяти, но проще система от этого не стала, да и проблему кеширования это не решает, а лишь усугубляет ситуацию.
Возможны и другие более или менее изобретательные решения, но то что я хочу вам предложить я пока не встречал. В свое время мне бы очень помогла подобная статья, поэтому я и решил её создать. Думаю что людям с похожей задачей она окажется полезной!
Моя реализация
Основная идея в том, что нам придется немного изменить логику запроса к базе, при этом не потребуется добавлять новые сущности или добавлять новые параметры в запрос.
Обновляем код на сервере
Для начала решим проблему кеширования, для этого просто всё перевернем.
Теперь последняя страница станет страницей номер 0, а предпоследняя страница номером 1, слово страница (page) сюда уже не вписывается, т.к мы с детства привыкли что в книжках страницы идут с начала, поэтому используем более нейтральное слово offset (смещение).
Теперь сколько постов мы бы ни добавили, GET “/?offset=0” всегда будет возвращать один и тот же результат.
Получать первую страницу стало немного сложнее, поэтому совместим оба выше приведенных способа, для этого перейдем с уровня запроса к базе на уровень сервера (язык nodejs):
Чего мы этим добились:
Теперь перекрытия id после добавления новых элементов стали невозможны.
Запросы теперь статичны и легко поддаются кешированию, единственным плавающим по количеству элементов и их id остался запрос без параметра offset.
Наш код на клиенте теперь не работает(
Минусы моего способа:
Вопрос что делать при удалении все ещё открыт, это не частая операция, поэтому можно каждый раз полностью сбрасывать кэш, либо возвращать null вместо отсутствующего элемента, это неплохое решение, т.к. зачастую реального удаления с сервера не происходит, элемент лишь помечается как удаленный, если таких “null-зомби” станет много, то можно удалить все null-зомби из выдачи и сбросить кэш для всех запросов.
Если новый элемент оказывается не в начале после сортировки по выбранному полю (например по названию), то данный алгоритм не сработает. Поэтому подходит только сортировка по возрастающим или убывающим полям (например по дате или по id).
Обновляем код на клиенте
Заодно я покажу как сделать бесконечную прокрутку из пункта №2.
Теперь у нас есть полностью лишенная обозначенных недостатков реализация. Несомненно присутствуют моменты которые можно сделать лучше, я хотел показать сам подход, реализация может быть у каждого разной.
Бонус: Добавляем гибкую систему перехода по страницам
В данный момент мы можем перемещаться лишь на 1 страницу вперед или назад, добавим возможность перейти на любую страницу, элемент управления может выглядеть примерно так (в квадратных скобках текущая страница):
Основа метода для генерации пагинации взята из этого обсуждения: https://gist.github.com/kottenator/9d936eb3e4e3c3e02598#gistcomment-3238804 и скрещена с моим решением.
Показать продолжение бонуса
В начале вам нужно добавить этот вспомогательный метод внутрь тега
Теперь, при необходимости, можно перейти на нужную страницу.








