PHPFAQ  
Начинающим   Технологии   MySQL   Ошибки   Ссылки   Юмор   О сайте   Форум   PHP Club  

Как разбить вывод из mysql постранично


Как сделать постраничный вывод из mysql "как в яндексе"?
по 10 записей на страницу, внизу - ссылки на остальные страницы?

Сначала научимся получать из базы нужные записи.
Их получение в mysql обеспечивается оператором LIMIT, который вызывается с двумя параметрами - с какой записи начинать, и сколько выводить (внимание! не по какую, а сколько!)
SELECT * FROM table LIMIT 0,10
этот запрос вернет записи с первой по 10, поскольку нумерация начинается с 0
соответственно, запрос для третьей страницы будет выглядеть, как
SELECT * FROM table LIMIT 20,10
получается, что нам всего лишь надо передать в скрипт число, которое потом подставить в запрос.
Этим будет заниматься код, который выводит ссылки на страницы.
Естественно, в цикле.
Для цикла нам понадобится количество записей, которое возвращает запрос без лимита.
Это число можно получить двумя путями. Либо отдельным запросом, в котором отсутствует оператор LIMIT, а вместо перечисления полей после оператора SELECT запрашивается только count(*):
$q="SELECT count(*) FROM table";
$res=mysql_query($q);
$row=mysql_fetch_row($res);
$total_rows=$row[0];


Либо, если версия mysql больше 4.0, то общее количество строк можно запросить в том же запросе. См. документацию mysql по функции FOUND_ROWS()
Однако, первый сособ представляется более удобным, хотя и немного более медленным.

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

Для начала определим, сколько всего получится страниц. Для этого надо поделить общее число записей на количество оных на одной странице и округлить результат в большую сторону. Таким округлением занимается в пхп функция ceil()
$num_pages=ceil($total_rows/$per_page);
В этом выражении участвует переменная $per_page, в которую мы положим количество выводимых на странице записей.
Ведь, если это количество изменится, мы же не хотим ползать по всему коду и исправлять цифры? проще сделать это один раз в начале скрипта при объявлении переменной. В запрос, вторым параметром LIMIT, подставлять нужно, конечно же, тоже ее.

Ну, а дальше, собственно, вывод ссылок.
for($i=1;$i<=$num_pages;$i++) {
  echo 
'<a href="'.$_SERVER['PHP_SELF'].'?num='.$i*$per_page.'">'.$i."</a>\n";
}

в цикле от 1 до $num_pages выводим ссылку с параметром num, равным числу, которое надо передать в LIMIT, а в тексте ссылки пишем номер страницы, поскольку людям понятнее видеть номер страницы, а не записи. На код это не влияет, а людям приятно.

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

что у нас в результате получилось?

// количество записей, выводимых на странице
$per_page=10;
// получаем номер страницы
if (isset($_GET['page'])) $page=($_GET['page']-1); else $page=0;
// вычисляем первый оператор для LIMIT
$start=abs($page*$per_page);
// составляем запрос и выводим записи
// переменную $start используем, как нумератор записей.
$q="SELECT * FROM `table` ORDER BY field LIMIT $start,$per_page";
$res=mysql_query($q);
while(
$row=mysql_fetch_array($res)) {
  echo ++
$start.". ".$row['field']."<br>\n";
}

// дальше выводим ссылки на страницы:
$q="SELECT count(*) FROM `table`";
$res=mysql_query($q);
$row=mysql_fetch_row($res);
$total_rows=$row[0];

$num_pages=ceil($total_rows/$per_page);

for(
$i=1;$i<=$num_pages;$i++) {
  if (
$i-== $page) {
    echo 
$i." ";
  } else {
    echo 
'<a href="'.$_SERVER['PHP_SELF'].'?page='.$i.'">'.$i."</a> ";
  }
}


Разумеется, вышеприведённый код подходит только как учебное пособие. С его помощью становится понятным принцип, но в реальных условиях мы сразу же столкнемся, как минимум, с двумя проблемами:
Во-первых, кроме переменной $page нашему крипту явно будут переданы и другие переменные, да и адрес может совсем не совпадать с именем скрипта. А мы это при формировании ссылок не учитываем.
Во-вторых, нормальный современный сайт немыслим без шаблонов. И такая ужасная лапша из SQL запросов, PHP кода и HTML тегов никуда не годится.

Займемся решением этих проблем.
Первая решается просто:
$uri strtok($_SERVER['REQUEST_URI'],"?")."?"
if (
count($_GET)) foreach ($_GET as $k => $v) if ($k != "page"$uri.=urlencode($k)."=".urlencode($v)."&";

и полученную переменную $uri подставляем в код вместо $_SERVER['PHP_SELF']

Вторая - тоже несложно. Шаблонизаторов много, но мы воспользуемся самым универсальным - PHP.
Что же у нас получилось? А получился у нас - рефакторинг! Переделка старого кода в соответствии с требованиями современности, плюс мелкое причесывание:

<?
//определим фрагмент запроса, который отвечает за то, какие записи мы запрашиваем
$from_where="FROM table WHERE filter=1";
// и получим общее количество записей
$res=mysql_query("SELECT count(id) ".$from_where);
$row=mysql_fetch_row($res);
$total_rows=$row[0];

//дальше получаем номер страницы и значение для лимита 
if (isset($_GET['page'])) $CUR_PAGE=($_GET['page']); else $CUR_PAGE=1;
$start=abs(($CUR_PAGE-1)*$per_page);

//выполняем запрос и получаем данные для вывода
$query="SELECT * $from_where ORDER BY date DESC LIMIT $start,$per_page";
$res=mysql_query($query);
while (
$row=mysql_fetch_array($res)) $DATA[++$start]=$row;

//определяем адрес страницы без переменной page
$uri=strtok($_SERVER['REQUEST_URI'],"?")."?";
if (
count($_GET)) {
  foreach (
$_GET as $k => $v) {
    if (
$k != "page"$uri.=urlencode($k)."=".urlencode($v)."&";
  }
}
  
//узнаем общее количество страниц и заполняем массив со ссылками
$num_pages=ceil($total_rows/$per_page);
for(
$i=1;$i<=$num_pages;$i++) $PAGES[$i]=$uri.'page='.$i;

//а дальше выводим в шаблоне днные и навигацию:
?>
Найдено сообщений: <b><?=$total_rows?></b><br><br>
<? foreach ($DATA as $i => $row): ?>
<?=$i?>
. <a href="?id=<?=$row['id']?>"><?=$row['title']?></a><br>
<? endforeach ?> 

<br>
Страницы: 
<? foreach ($PAGES as $i => $link): ?>
<? 
if ($i == $CUR_PAGE): ?>
<b><?=$i?></b>
<? else: ?> 
<a href="<?=$link?>"><?=$i?></a>
<? endif ?> 
<? endforeach ?> 



Другие материалы раздела:
\"Кавычки \". Cоставление запросов, слеши, SQL Injection
Проблемы с кодировкой в MySQL версий 4.1+
Пример кода, работающего с MySQL
Базовые понятия MySQL и отличия от текстовых файлов.


Комментарии

Дима 26.12.11 18:48
А в чем собственно недостатки первого метода?

У меня не получается второй, а первый вроде ничего так работает
Анатолий 04.11.11 21:29
Спасибо за статью. Очень полезная. :)
Ramzil_Nixon 22.10.11 10:06
Я не понял, как это работает. Обясните чайнику понятным языком. Спасибо
Эд 01.09.11 17:08
а все, понял. автор не надо отвечать. спасибо за статью!
Эд 01.09.11 17:00
Автор, поясните почему происходит такое - при выводе списка страниц, ссылка на первую отсутствует?
Есть на вторую, третью и т.д., а на первую нет.
Валентин 22.08.11 13:39
Классный материал.
Скажите а вот такая ситуация.
Есть таблица пользователей. На ней реализована постраничная навигация.
Есть поля поиска по логину и дате регистрации.
Так вот если сделать выборку (поиск), например по дате, то на первой странице запроса навигация выведется красиво, пока не попробуешь по ней перейти. Переходишь например на вторую страницу - появляются все пользователи те запрос сбивается. Какие есть советы?
Ответ: нужно обязательно добавлять все параметры поиска к ссылке
Юрий 07.08.11 16:54
Подскажите,пожалуйста, как сделать, чтобы вывод шел от последней записи к первой, т.е. в обратном порядке.
Ответ: добавить в запрос ORDER BY поле DESC
Лиз 17.06.11 12:39
$row=mysql_fetch_row($res);
Тоже ошибка следующего содержания: "Warning: mysql_fetch_row(): supplied argument is not a valid MySQL result resource in...". Подскажите, с чем это связано и как исправить??
Ответ: первоначальные действия описаны здесь: http://phpfaq.ru/debug
дальше надо смотреть по обстоятельствам.
ярик 29.03.11 14:03
выаф
Валерий 04.02.11 16:28
Здравствуйте! А подскажите, пожалуйста, как сделать следующую простую вещь: при использовании "хлебных крошек" все отлично работает на страницах статей, статичных страницах, странице поиска и архивов, НО не работает, при переходе по страницам навигации, т.е. так называемых страница page 1, page 2 и т.д. Эти страницы на Вашем блоге отображаются на главной странице в виде квадратиков. Нажимаем на 2, и видим в строке браузера "http://www.example.com/page/2/" - т.е. мы находимся на 2-й странице. Так вот именно на этих страницах мне не удается добиться корректной работы "хлебных крошек".
Я решил пойти таким путем: встроить в шаблон темы там, где осуществляется вывод страниц начиная со 2-й следующий код:
<a title="Перейти на Главную" href="http://glavnaya-stranitsa">Главная</a> &gt; Страница <PHP КОД ВЫВОДА ТЕКУЩЕЙ СТРАНИЦЫ>, но в силу плохого знания php-функций, не понимаю, что надо прописать в области кода вывода номера текущей страницы.
Подскажите, что туда надо "приляпать"?
Ответ: Сложный вопрос. Я бы лучше его задал на форуме, чем здесь.

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

© phpfaq.ru, 2012
Rambler's Top100 0.209 sec.