Сайт о телевидении

Сайт о телевидении

» » Постраничный вывод html. Постраничный вывод контента в браузер

Постраничный вывод html. Постраничный вывод контента в браузер

/* 09.07.2008 */

Постраничный вывод (PHP и MySQL)

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

Для начала отмечу, что статья не учит работе с базой данных и PHP, а даёт объяснение реализации, и приводит готовый к использованию (постраничной навигации).

Начнем! Допустим имеется база данных (MySQL), например, с объявлениями. Нам нужно реализовать их отображение на сайте, порциями по 20 штук на странице. Для перехода же между порциями в нижней части каждой страницы необходимо сформировать ссылки с номерами «порций» (ярлыки страниц):

Поехали...

Выборка данных порциями

Для выборки ВСЕХ объявлений из базы требуется запрос вида:

SELECT * FROM table1

Конечно, это упрощенный вариант, и в реальных задачах, чаще всего, в запросе присутствуют различные условия (операторы WHERE , ORDER BY ...).

Для того чтобы этот запрос делал выборки порциями, необходимо к нему добавить оператор LIMIT :

Синтаксис оператора LIMIT: LIMIT row_count

Необязательный параметр offset сообщает сколько рядов от начала выборки нужно пропустить, а row_count указывает сколько рядов нужно выбрать, т.е. LIMIT 0, 20 (или просто LIMIT 20 опустив нулевой offset ) выбирает первые 20 рядов (с 0 по 19 ряд), а LIMIT 40, 20 указывает пропустить 40 (с 0 по 39 ряд) и выбрать следующие 20 (т.е. будут выбраны ряды с номера 40 по 59).

Обращаю ваше внимание, что ряды в выборке нумеруются с нуля, а не с единицы.

Таким образом запросы для нашего примера с объявлениями будут следующими:

#запрос для выборки страницы 1: SELECT * FROM table1 LIMIT 0, 20 #запрос для выборки страницы 2: SELECT * FROM table1 LIMIT 20, 20 #запрос для выборки страницы 3: SELECT * FROM table1 LIMIT 40, 20

и.т.д. offset увеличиваем на 20 для каждой следующей страницы, а row_count всегда равен 20.

Ещё необходимо отметить, что оператор LIMIT в запросе идет по порядку после WHERE , GROUP BY , HAVING , ORDER BY , но если вы новичок в MySQL, то можно сказать, что он идет в конце строки запроса (после него идут операторы довольно редко используемые).

Вторая часть с которой нам нужно разобраться это строка с ярлыками страниц...

Ярлыки страниц

Например, для выборки третьей двадцатки объявлений ярлык может иметь следующий вид:

страница №3

При клике по этой ссылке запускается скрипт obyavleniya.php, которому доступен параметр page_number, сообщающий, что запрашивается 3 двадцатка объявлений - 3 страница. Скрипт пропускает первые 40 объявлений, и выбирает следующие 20.

Для вывода этой строки ярлыков требуется знать общее число страниц (чтобы знать сколько ярлыков "рисовать"). Его мы можем получить, разделив общее число объявлений на количество объявлений на странице, округлив результат до большего целого. Т.е., если в нашем примере, допустим, всего 107 объявлений, а выводим мы их на каждой странице по 20 штук, то число страниц будет: 107 / 20 = 5.35, т.е. 5 полных страниц (по 20 объявлений) + одна неполная (7 объявлений), итого, округлив получаем 6 страниц (соответственно будет 6 ярлыков).

Для подсчета общего числа объявлений, есть два пути. Первый путь - выполнить отдельный суммирующий запрос практически аналогичного запросу для выборки данных, только без ограничивающего оператора LIMIT , и ненужных операций сортировки (ORDER BY), например:

#запрос для выборки объявлений 3 страницы SELECT * FROM table1 WHERE category_id="89" AND ... ORDER BY publish_date DESC LIMIT 40, 20 #запрос для подсчета ВСЕХ объявлений в базе SELECT COUNT(*) FROM table1 WHERE category_id="89" AND ...

Первый запрос выполняет выборку объявлений, а второй, подсчитывает их общее число функцией COUNT . На практике запросы выборки данных бывают весьма громоздкими и тяжелыми, поэтому дополнительный тяжелый запрос для подсчета не самая «необходимая» операция. Так же этот путь не такой изящный как второй...

В MySQL 4.0.0 появились замечательные вещи, такие как функция FOUND_ROWS и связанная с ней SQL_CALC_FOUND_ROWS - опция оператора SELECT .

Рассмотрим второй вариант подсчета общего числа рядов:

SELECT SQL_CALC_FOUND_ROWS * FROM table1 WHERE category_id="89" AND ... ORDER BY publish_date DESC LIMIT 40, 20 SELECT FOUND_ROWS()

Опять же, первый запрос делает выборку объявлений, а второй получает их общее число, но...

Запрос выборки объявлений в данном случае отличается от выборки из первого варианта только наличием опции SQL_CALC_FOUND_ROWS . Данная опция указывает MySQL вместе с выборкой данных сделать и подсчёт всех тех строк которые бы вернул запрос без оператора LIMIT . Т.е. по сути данный запрос включает в себя в скрытом виде COUNT запрос из первого варианта. При этом сама подсчитанная сумма не возвращается, а запоминается сервером. Теперь, для того чтобы узнать это число, нужно выполнить запрос с функцией FOUND_ROWS (при этом сервер не производит никаких вычислений, просто отдает то, что запомнил раньше).

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

Собираем все вместе

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

  1. первым делом при запуске скрипта смотрим какую страницу запрашивает пользователь (в нашем примере на это указывает параметр page_number);
  2. на основании номера запрашиваемой страницы вычисляем параметр offset оператора LIMIT ;
  3. запускаем запрос выборки объявлений с оператором LIMIT offset, 20 (где, 20 - это количество отображаемых объявлений на странице в нашем примере);
  4. получаем общее число объявлений в базе;
  5. на основании пункта 4 вычисляем общее число страниц объявлений и формируем строку ярлыков.

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

PHP класс Paging для постраничной разбивки

Теперь я приведу пример как организуется постраничная навигация с использованием PHP-класса Paging .

//подключаем класс Paging require("paging.inc.php "); //соединяемся с базой данных $_DB = new mysqli($host,$user,$passwd,$db_name); //создаем экземпляр класса Paging //в качестве параметра передаем ему указатель на соединение с MySQL $_PAGING = new Paging($_DB); //выполняем обычный запрос данных не заботясь //о разбивке на страницы через метод get_page объекта класса Paging $r = $_PAGING->get_page("SELECT * FROM table1"); while($row = $r->fetch_assoc()) { //обрабатываем КАК ОБЫЧНО полученные из базы данные и выводим пользователю } //выводим информационную строку вида: "Показано с 1 по 20 из 107" echo $_PAGING->get_result_text()." объявлений"; //выводим ссылки-ярлыки на предыдущую и следующую страницы echo "Страницы: ".$_PAGING->get_prev_page_link()." ".$_PAGING->get_next_page_link()."

"; //а также строку с номерами страниц (основные ярлыки) echo $_PAGING->get_page_links(); ?>

Единственное чем данный скрипт отличается от обычного скрипта без постраничной разбивки, так это тем, что запрос выборки данных которые нужно разделить на части производится не через mysqli->query() , а через метод get_page() реализованный в классе Paging, а так же тремя последними строками которые отображают ярлыки и строку отчет о выборке.

Постскриптум

P.S.: Этот постскриптум я привожу скорее для полноты изложения, нежели как реально актуальную информацию для большинства читателей.

Применение SQL_CALC_FOUND_ROWS и FOUND_ROWS() имеет некоторые подводные камни при использовании в UNION-запросах, так как операторы LIMIT могут использоваться в нескольких местах, и могут касаться как отдельных операторов SELECT в составе UNION, так и общего результата UNION в целом. Цель же SQL_CALC_FOUND_ROWS для UNION состоит в подсчёте количества строк, которые будут возвращены без глобального LIMIT . Поэтому следует привести условия применения SQL_CALC_FOUND_ROWS с запросами UNION:

  • Ключевое слово SQL_CALC_FOUND_ROWS должно указываться в первом операторе SELECT ;
  • Значение FOUND_ROWS() будет точным только при условии применения UNION ALL . Если указано UNION без ALL , происходит исключение дубликатов, и значение FOUND_ROWS() будет лишь приблизительным;
  • Если в UNION не присутствует LIMIT , то SQL_CALC_FOUND_ROWS игнорируется и возвращается количество строк во временной таблице, которая создается для выполнения UNION .

Регулярно в форуме задают один и тот же вопрос: как сделать постраничный вывод. И каждый раз человеку отвечают: "Легко! m строк, начиная с n-ной: Select запрос Limit $n,$m". На самом деле не так всё просто.

Я уже писал про синтаксис параметра LIMIT, однако, без толку. Для полноценного постраничного вывода строк из базы требуется большее. Требуется


  • Обработать номер страницы (в том числе проверить, не больше ли он общего количества страниц)
  • Нарисовать навигационную строку (чтобы не просто "вперед-назад", а с ссылками на несколько соседних страниц)

Тут-то и начинаются главные проблемы.

Недавно я работал над сайтом, в котором эти постраничные выводы в статистике были в каждом списке (а списков было много!). Тут-то и созрело решение, как свести все эти штучки к простому и единому решению. Получились четыре функции, которые я теперь использую везде, где нужен постраничный вывод данных, и не напрягаю попусту голову проблемой (как же я делал это там, как бы вынуть этот код оттуда?).

Первая функция — для внутреннего пользования двумя следующими. Берёт номер страницы, общее количество строк и количество строк на странице и выдаёт номер страницы, уже проверенный. Вторая берёт то же самое, проверяет номер страницы и выдаёт парамерт LIMIT либо полный (LIMIT n,m), либо краткий (LIMIT m), если это первая страница, либо ничего не выдаёт. Третья функция из тех же трёх параметров и адреса для ссылки делает навигационную строку. Ещё одна функция выдаёт число для нумерованного списка.

Этого достаточно для нормальной работы с постраничным выводом данных. Посмотрим, что получается в коде программы:


// кол-во строк в страницах

$in_page = 10 ;

// получаем количество строк

$amount = @ mysql_result (mysql_query ("SELECT count(id) as goods_total FROM goods" ), 0 );

// рисуем навигационную строку и пишем начало таблицы

Print("

" . < b > draw_bar ($page , $amount , $in_page ,

"goods.php?page=" ). "

\n" );

// формируем запрос к базе

$goods_result = mysql_query ("SELECT id, name, description, price FROM goods

ORDER BY name, price " . < b > get_limit ($page , $amount , $in_page ));

// получаем номер для нумерованного списка

$count = < b > get_count_limit ($page , $amount , $in_page );

// выводим строки

While ($good_row = mysql_fetch_array ($goods_result )) {

$count ++;

Print ("

// фон каждой второй строки — серым цветом

If ($count / 2 == intval ($count / 2 ))

Print (" bgcolor=#e1e1e1" );

Print (">

\n" );

// конец таблицы и нижняя навигационная строка

Print("

$count.${good_row}


${good_row}

${good_row}
" . < b > draw_bar ($page , $amount ,

$in_page , "goods.php?page=" ). "

\n" );

Это ВСЁ, что нужно для постраничного вывода! Больше напрягаться не надо!

Одно только пояснение — в качестве параметра функции draw_bar указывается адрес этого скрипта со всеми параметрами так, чтобы он туда только дописывал номер страницы. Если сложная выборка, надо будет ручками формировать этот адрес (всё-таки упрощение жизни вышло относительное: упрощаем одно — усложняем другое).

Навигационная панель сделана в виде номеров страниц (" 1 | 2 | 3 "). Но привести к виду "0-10 | 11-20 | 21-30" не проблема.

Делаем постраничный вывод на Php + Mysql

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

Это было для меня огромной проблемой и для ее решения я оббегал кучу форумов и сайтов. Постраничный вывод очень нужен и в наше время, но статей по нему до сих пор почти нет. Я решил исправить эту несправедливость. На своем примере я покажу вам постраничный вывод сообщений из mysql-базы, т.к. сейчас она очень популярна и имеет множество преимуществ по сравнению с файлами. Если вам будет нужно, то я могу показать вам постраничный вывод сообщений из файла.

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

id text
1 text1
2 text2
3 text3
4 text4
5 text5

С базой определились, можно начинать скрипт. Для начала подключимся к нашей новоиспеченной базе и вычислим число строк в таблице posts.
$link = mysql_connect("localhost","user","pass"); //Подключаемся к серверу базы данных
mysql_select _db("base",$link); //Выбираем базу
$posts = mysql_query("select * from posts");
$total_posts = mysql_num_rows($posts); //Считаем число строк в таблице с сообщениями (posts)

Итак, начало положено. Теперь будем писать функцию для отображения ссылок на другие страницы с сообщениями, собственно говоря сам постраничный вывод. Это не слишком сложная функция, но и не самая простая, т.к. меньше она быть не должна, а больше по вашему усмотрению. Мы задаем функции 4 аргумента (в комментарии они описаны). Вы можете добавить несколько примочек в эту функцию на свой вкус я дал вам лишь основу, дальше двигайтесь сами. Можно например выделить ссылку на текущую страничку.

function print_links($total,$page,$number,$links) //1 аргумент - всего сообщений, 2 - номер текущей страницы, 3 - число сообщений на странице, 4 - число отображаемых ссылок
{
$return = null;
$pages = ceil($total/$number); //Вычисляем сколько должно получиться страниц

if ($page+$links <= $pages)
{
$start = $page;
}
else
{
$start = $pages-($links-1);
$finish = $pages;
}

if ($start < 0)
{
$start = 1;
}

for ($i=$start;$i<=$finish;$i++) //Записываем в переменную ссылки
{
$return .= " ".$i." ";
}
return $return; }

Итак, функция у нас готова. В ней вы можете указать сколько ссылок будет отображаться и сколько сообщений выводить. Осталось самое легкое: надо вывести сообщения в браузер, с учетом номера страницы, на которой сейчас посетитель.

Для начала добавим небольшую проверку. Если у вас не указан номер страницы, то по умолчанию вам будет отображаться первая страница. МОжно добавить также дополнительные проверки, чтобы исключить работу скрипта при запросах вида: $#$## или фав1324. Это повысит безопасность скрипта, а следовательно и сайта.

if (!isset($page)) { $page = "1"; } //Здесь мы делаем проверку. Если переменная page (номер страницы) не указана, то ей присваивается значение 1
print print_links($total_posts,$page,"10","5"); //Выводим в браузер ссылки
$query = mysql_query("select * from posts limit ".(($page-1)*10).",10 "); //Выбираем нужное число сообщений
while ($row=mysql_fetch_array($guest)) { print "
".$row["text"]."
"; //Выводим сообщения (дизайн делайте сами) }
mysql_close($link);
?>

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

Если что-то непонятно, то заходите ко мне на сайт или пишите мне на email





Половина второго ночи, несколько дней недосыпа... И вдруг озарение - наконец-то я понял, какой-такой супер-пупер скрипт мне нужен для раскрутки моего нового сайта. Сказано - сделано, сажусь... нет не писать этот супер-пупер скрипт:) Программисты народ ленивый, спецы по раскрутке тем более (отсюда и баннерообменные сети, авторегистраторы в каталогах и пр. лентяйские штучки). В общем лезу на Google и начинаю методично обшаривать Рунет в поисках требуемого. Проходит час, два... В душу закрадываются смутные подозрения, плавно перерастающие в уверенность, что требуемый мне скрипт в природе либо не существует, либо спрятан на чьем-то заповедном локалхосте с правами доступа 000.

Разбиваю стоящую перед скриптом задачу на несколько составных и минут за 15 пишу реализацию большинства его (скрипта) функций:) Радостно берусь за следующую фичу... В общем, необходимо всего-то навсего обеспечить постраничный вывод контента. То есть, например, имеется 60 статей и нужно эти статьи выводить по 10 на страницу и построить меню для оного вывода в виде "1 2 3 ..."

Четыре утра, окромя заветного "LIMIT" в голову ничего не приходит. И дело даже не в том, что БД к моему скрипту никакого отношения не имеет. С горечью понимаю, что квалификация потеряна, лучшие годы жизни прожиты впустую и... иду на Google ;)
После... в общем уже наступил рассвет. К этому времени я познакомился с кучей литературы по данному вопросу, но везде ситуация так или иначе рассматривалась в контексте работы с SQL, либо я постоянно наталкивался на какую-то растиражированно-заумную статью про "отделение алгоритма постраничного вывода от контекста или-что-то-в-этом-роде" (ИМХО, нижеприведенные 7 строчек кода эту проблему решают) в которой ничегошеньки не понял... :(И вот когда я уже отчаялся и решил что утро вечера мудренее, на каком-то дремучем, заросшим мхом форуме (адрес к сожалению канул в Лету - по крайней мере у меня его нет), наткнулся на переписку некоего вечно извиняюшегося Программера и безумного и злобного Модератора - тема переписки была посвящена именно построению меню для постраничного вывода. И это было именно то что нужно!

В общем, ниже по тексту я исправляю несправедливость: теперь в Интернете есть статья о том, как обеспечить построение меню для постраничного вывода! Простой рабочий код на PHP, с подробными комментариями. Никакой излишней функциональности типа ссылок "next page". Сделано сие намеренно - ни разу в жизни не нажимал на "next" если можно было нажать на номер страницы - а данная статья написана с целью дать возможность понять основной принцип, т.е. чем меньше кода, тем лучше (хотя кнопка "next" и добавляется тремя строками кода).

Соглашения о комментариях к коду:

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

Таким образом, на экране браузера мы получили что-то вроде "1 2 3 4 5 6 7 8", при этом URL (на моем компьютере, при выбранной третьей "странице") выглядит так: http://localhost/ep/ep.php?from=20 . Обратите внимание, на то, что странице под номером "3" соответствует значение from=20 то есть отсчет выводимых статей будет вестись в данном случае с 20-й.

Вообще, как вы уже поняли, переменная $from играет здесь ключевую роль. Она участвует в построении ссылок на каждую отдельную страницу (если, конечно, можно так выразиться:), и самое главное - дает нам точку отсчета для вывода статей. Например (и наверняка вы сделаете именно так) указатели на статьи (или сами статьи;) можно поместить в массив, с последующей выборкой и выводом в браузер по 10 статей, начиная от значения $from.

То есть еще раз иными словами: весь огород собственно и городился ради того, чтобы дать скрипту данные относительно того на какой "странице" сейчас находится посетитель и начиная с какого номера "статьи" следует вывести заданное количество этих статей на страницу - а это и есть значение переменной $from. Т.е. при $from=20 выведутся страницы с 21 по 30 (это также зависит от реализации вывода и значения переменной $articles_per_page)