Сегодня я решил рассказать о том как можно легко организовать постраничную навигацию в PHP.
Пожалуй каждый разработчик веб приложений сталкивался с задачей постраничного вывода контента на страницах приложения. В целом алгоритм данной задачи всегда примерно одинаков.
1) Определяем текущую страницу и сколько записей мы выводим на странице.
2) Определяем сколько записей у нас всего.
3) Делаем выборку по записям текущей страницы.
4) Выводим ссылки на другие страницы.
Если преобразовать данные строки в код PHP, то выглядеть это будет примерно так:
//Определяем текущую страницу $page = !empty($_REQUEST['page']) && is_numeric($_REQUEST['page']) && $_REQUEST['page'] > 0 ? $_REQUEST['page'] : 1; //Определяем сколько записей выводить на странице $onPage = 10; //Формируем условия выборки из базы $where = 'WHERE field1 = "'.strip_tags($_REQUEST['filter1']).'" AND field2 = "'.strip_tags($_REQUEST['filter2']).'"'; //Узнаем сколько всего записей удовлетворяет нашей выборке $count = mysql_query('SELECT COUNT(*) as c FROM table '.$where); $count = mysql_fetch_assoc($count); $count = $count['c']; //Получаем строки выбранной страницы $rows = mysql_query('SELECT * FROM table '.$where.' LIMIT '.(($page - 1) * $onPage).', '.$onPage); while($row = mysql_fetch_assoc($rows)) { //Вывод строк } //Определим сколько страниц всего $pagesCount = ($count % $onPage == 0 ? $count / $onPage : ((int)($count / $onPage)) + 1; //Выводим список страниц for($i = 1; $i <= $pagesCount; ++$i) { echo $i == $page ? $i : '<a href="/linkToScript.php?page='.$i.'&filter1='.strip_tags($_REQUEST['filter1']).'&filter2='.strip_tags($_REQUEST['filter2']).'">'.$i.'</a>'; }
Данная реализация вполне имеет место быть, однако есть в ней один момент, который часто «портит всю малину». Этот момент — необходимость генерировать запрос получения количества всех записей, удовлетворяющих текущим фильтрам (три строчки с $count). Весь подвох заключается в том, что строку условий $where не всегда так легко получить и подставить в запрос «SELECT COUNT». В данном случае приходиться включать смекалку и выкручиваться любыми доступными методами.
Я хочу рассказать Вам об одной прекрасной конструкции языка mysql, которая позволяет решить данную проблему и практически не приведет к изменению примера выше. Речь идет о ключевом слове SQL_CALC_FOUND_ROWS который можно использовать в запросе SELECT, если в нем присутствует ключевое слово LIMIT. Данное ключевое слово говорит Mysql сохранить число общего число записей, которое было выбрано запросом до применения ограничения LIMIT (если Вы не в курсе, то ограничение LIMIT срабатывает после выборки всех строк запроса). Для получения данного сохраненного числа так же используется запрос, который сработает гораздо быстрей, чем запрос SELECT COUNT.
В конечном итоге наш код будет модифицирован следующим образом:
//Определяем текущую страницу $page = !empty($_REQUEST['page']) && is_numeric($_REQUEST['page']) && $_REQUEST['page'] > 0 ? $_REQUEST['page'] : 1; //Определяем сколько записей выводить на странице $onPage = 10; //Формируем условия выборки из базы $where = 'WHERE field1 = "'.strip_tags($_REQUEST['filter1']).'" AND field2 = "'.strip_tags($_REQUEST['filter2']).'"'; //Получаем строки выбранной страницы $rows = mysql_query('SELECT SQL_CALC_FOUND_ROWS * FROM table '.$where.' LIMIT '.(($page - 1) * $onPage).', '.$onPage); //Получаем общее число записей, полученное в последнем запросе до применения ограничения LIMIT $count = mysql_query('SELECT FOUND_ROWS() as c'); $count = mysql_fetch_assoc($count); $count = $count['c']; while($row = mysql_fetch_assoc($rows)) { //Вывод строк } //Определим сколько страниц всего $pagesCount = ($count % $onPage == 0 ? $count / $onPage : ((int)($count / $onPage)) + 1; //Выводим список страниц for($i = 1; $i <= $pagesCount; ++$i) { echo $i == $page ? $i : '<a href="/linkToScript.php?page='.$i.'&filter1='.strip_tags($_REQUEST['filter1']).'&filter2='.strip_tags($_REQUEST['filter2']).'">'.$i.'</a>'; }
Как видно, изменения минимальны. Однако удобство данного подхода на лицо 🙂