На главную

Библиотека Интернет Индустрии I2R.ru

Rambler's Top100

Малобюджетные сайты...

Продвижение веб-сайта...

Контент и авторское право...

Забобрить эту страницу! Забобрить! Блог Библиотека Сайтостроительства на toodoo
  Поиск:   
Рассылки для занятых...»
I2R » Сайтостроительство » Web-программирование

Разбиение результата запроса на страницы

Продолжение введения в объектно-ориентированное программирование на PHP.

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

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

Допустим, у нас есть база данных шуток, и мы имеем таблицу Jokes (в ней столбец AID связывает эту таблицу с таблицей Authors).

навигация по страницам

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

Вот так будет выглядеть код страницы, если мы используем ООП:

<?php
require('pagedresults.php');

$cnx = @mysql_connect('localhost','kyank','********');
mysql_select_db('jokes',$cnx);
$rs = new MySQLPagedResultSet("select * from jokes where aid=$aid",
5,$cnx);

?>

<html>
<head>
<title>Paged Results Demo</title>
</head>
<body>
<table border="1">
<?php while ($row = $rs->fetchArray()): ?>
<tr><td><?=$row['JokeText']?></td><td><?=$row['JokeDate']?></td></tr>
<?php endwhile; ?>
</table>
<p><?=$rs->getPageNav("aid=$aid")?></p>
</body>
</html>

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

require('pagedresults.php');

Как и в примере с прямоугольником, мы помещаем код класса в отдельный файл. Это сделано не только для упрощения работы с ним, но и для того, чтобы этот класс можно было использовать на других страницах простым подключением, а не тупым copy/paste.

После подключения к базе данных мы создаем объект:

$rs = new MySQLPagedResultSet("select * from jokes where aid=$aid", 5,$cnx);

Как вы видите, конструктор для этого класса принимает три параметра:

  • SQL-запрос
  • Число записей, которые мы хотим вывести на одну страницу
  • Указатель на соединение с базой

Мы могли бы спроектировать объект таким образом, чтобы передавать ему обычный результат MySQL запроса, но я решил встроить разбор запроса в объект, чтобы более наглядно проиллюстрировать работу класса.

После того, как получили массив из базы данных, затем обычно используют цикл while для прохода по всему массиву и вывода его содержимого. Наш класс разбиения по страницам (экземпляр которого мы назвали $rs) позволяет делать то же самое, используя метод fetchArray() точно так же, как мы обычно используем функцию mysql_fetch_array():

<?php while ($row = $rs->fetchArray()): ?>

Так что каждый вызов $rs->fetchArray() будет возвращать массив, содержащий одну строку из результата запроса (массив будет помещаться в переменную $row), пока не достигнет конца результатов на данной странице. В конце страницы $rs->fetchArray()вернет false, так что цикл закончится.

Все что осталось, это вывести ссылки на страницы:

<p><?=$rs->getPageNav("aid=$aid")?></p>

Метод getPageNav несет ответственность за вывод навигации, которые для страницы 4 будут выглядеть вот так:

пример таблицы MySQL

Метод создает ссылки на этот же скрипт, которые содержат специальную переменную resultinpage в строке запроса. Конструктор класса MySQLPagedResultSet отслеживает эту переменную и использует ее для того, чтобы определить, какую страницу из результата выборки показывать.

Так как в большинстве случаев SQL-запрос содержит больше одной переменной (в данном случае только переменную $aid, которая определяет шутки какого автора выводить), методу getPageNav можно передавать любые дополнительные элементы запроса. В нашем случае, мы передаем "aid=$aid", для гарантирования того, что переменная $aid будет передоваться через все сгенерированные ссылки.

Сейчас как мы видим, как должен работать объект, мы можем перейти к созданию самого класса MySQLPagedResultSet.

Создаем класс MySQLPagedResultSet

Мое описание интерфейса класса дало вам ключ к пониманию того, как класс устроен. Сейчас мы пройдемся по всему коду класса и узнаем, как он работает.

class MySQLPagedResultSet
{
var $results;
var $pageSize;
var $page;
var $row;

Начали мы со списка переменных (правильнее говоря, свойств):

  • $results используется для хранения результата MySQL запроса
  • $pageSize содержит количество записей, отображаемых на одной странице
  • $page хранит ссылку каждой страницы результата запроса
  • $row используется методом fetchArray для отслеживания текущей строки в результате запроса

Примемся за конструктор:

function MySQLPagedResultSet($query,$pageSize,$cnx)
{
global $resultpage;

Как мы решили ранее, конструктору будет передаваться три параметра: SQL-запрос, количество записей на странице, и соединение с базой данных.

Первое, что мы сделаем в этой функции, получим доступ к глобальной переменной $resultpage. Это переменная, которую метод getPageNav вставляет в ссылки для обозначения, какую страницу отображать.

Затем конструктор должен обработать строку запроса:

$this->results = @mysql_query($query,$cnx);

Результат помещается в $this->results, переменную класса, определенную ранее. Затем надо инициализировать свойство $this->pageSize, которому просто присваивается значение параметра $pageSize, передаваемого конструктору.

$this->pageSize = $pageSize;

После этого надо установить текущую страницу. Для начала проверим переменную $resultpage. Если переменная пуста или имеет отрицательное значение, мы присваиваем ей значение 1, так что первая страница будет выбираться по умолчанию.

if ((int)$resultpage <= 0) $resultpage = 1;

(int) заставляет PHP конвертировать значение переменной $resultpage в целое число. Это нужно для того, если кто-то изменит строку запроса и наберет там строковое значение. Мы также должны проверять, не выходит ли данная страница за пределы выборки. Для этого используем метод getNumPage:

if ($resultpage > $this->getNumPages())
$resultpage = $this->getNumPages();

В итоге, с корректным номером страницы на руках, мы передаем его методу setPageNum:

$this->setPageNum($resultpage);
}

С конструктором мы разобрались, перейдем к методам. Метод getNumPage определяет число страниц в данной выборке. Он нужен для служебного использования другими методами класса, в частности, в конструкторе.

function getNumPages()
{
if (!$this->results) return FALSE;
return ceil(mysql_num_rows($this->results) /
(float)$this->pageSize);
}

Как видите, определение числа страниц достаточно простая операция. Мы просто делим число строк в результате запроса (mysql_num_rows($this->result)) на размер страницы ($this->pageSize), а затем округляем результат, используя функцию ceil. Для того, чтобы быть уверенным, что результат деления можно будет округлить, мы конвертируем значение свойства pageSize в число с плавающей запятой.

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

function setPageNum($pageNum)
{
if ($pageNum > $this->getNumPages() or
$pageNum <= 0) return FALSE;

$this->page = $pageNum;
$this->row = 0;
mysql_data_seek($this->results,($pageNum-1) * $this->pageSize);
}

Вначале мы проверяем параметр $pageNum, чтобы удостовериться, относится ли он к диапазону возможных страниц. Затем сохраняем новый номер страницы в $this->page, сбрасываем $this->row на ноль, а затем используем функцию PHP mysql_data_seek для перехода к первой строке выбранной страницы.

Метод getNumPage возвращает текущий номер страницы.

function getPageNum()
{
return $this->page;
}

Зачем он нужен, спросите вы? В конце концов, если $rs - это экземпляр класса MySQLResultSet, то можно получить номер страницы вот так $rs->page, правильно? Давайте посмотрим на метод setPageNum. Как вы можете видеть, в процесс установки номера страницы вовлечено много чего. Если тот, кто использует класс, решить напрямую изменить $rs->page, у него ничего не выйдет и объект будет вести себя не так, как ожидалось.

Текущие представления об ООП говорят, что ни одно свойство не должно быть напрямую доступно за пределами класса. Вместо этого, методы должны предусматривать "считывание" и "установку" значений каждого свойства, доступ к которым может понадобиться извне. В большинстве объектно-ориентированных языков это можно навязать, объявив свойство как private, и поэтому совершенно недоступное извне.

В PHP такого нет, но принцип использования свойств от этого не меняется. Вместо доступа к свойствам напрямую, надо пользоваться методами.

На практике метод getPageNum можно использовать для вывода надписи "Страница X из Y". Код для этого будет такой:

<p>Страница <?=$rs->getPageNum()?> из <?=$rs->getNumPages()?>.</p>

Уф! Столько разговоров о таком простом методе! К счастью, следующие требуют гораздо меньше объяснений.

function isLastPage()
{
return ($this->page >= $this->getNumPages());
}

function isFirstPage()
{
return ($this->page <= 1);
}

Эти методы позволяют определить, является текущая страница последней или первой. Возвращают методы истину или ложь. А код должен сам все объяснять.

Осталось разобрать две рабочих лошадки класса. Для начала займемся методом fetchArray, который заменяет функцию mysql_fetch_array для нашего случая.

function fetchArray()
{
if (!$this->results) return FALSE;
if ($this->row >= $this->pageSize) return FALSE;
$this->row++;
return mysql_fetch_array($this->results);
}

Метод возвращает ложь, если результат запроса, хранящийся в $this->results, ложен (т.е. сам запрос был ложен), а также если текущий номер ряда выборки превышает или равен номеру текущей страницы (это свидетельствует о том, что достигнут конец страницы). Если проверки проходят, то значение текущего ряда увеличивается на единицу и вызывается функция mysql_fetch_array для возврата следующего ряда выборки.

Ну, и наконец метод getPageNav.

function getPageNav($queryvars = '')
{

Как видите, параметр $queryvars имеет значение по умолчанию пустую строку для того, чтобы сделать этот параметр необязательным, так что вы можете вызывать метод getPageNav без параметров, если не хотите передавать никаких переменных в строке запроса генерируемых ссылок.

if (!$this->isFirstPage())
{
$nav .= "<a href=\"?resultpage=".
($this->getPageNum()-1).'&'.$queryvars.'">Prev</a> ';
}

Прежде всего, если текущая страница не является первой (эту проверку мы делаем используя метод isFirstPage), нам надо отобразить ссылку "Prev". Строка запроса содержит волшебную переменную resultpage, которая говорит объекту ySQLPagedResultSet какую страницу отображать. После этой переменной пихаем в строку запроса все оставшиеся переменные, содержащиеся в параметре .$queryvars.

На выходе этого метода мы имеем переменную $nav, которую принтовать на страницу будем не в классе, а непосредственно в шаблоне. Это обеспечит некоторую гибкость. Далее собираем список страниц:

if ($this->getNumPages() > 1)
for ($i=1; $i<=$this->getNumPages(); $i++)
{
if ($i==$this->page)
$nav .= "$i ";
else
$nav .= "<a href=\"?resultpage=&".
$queryvars."\"></a> ";
}

Проверяем, что страниц больше чем одна. Выводить единицу на единственной странице совершенно бессмысленно. Затем используем цикл for для прохода по номерам страниц, делая ссылка на соответствующие номерам страницы, за исключением текущей страницы (где $i=$this->page), номер которой мы выводим без ссылки.

В заключение выводим ссылку "Next", если текущая страница не является последней.


if (!$this->isLastPage())
{
$nav .= "<a href=\"?resultpage=".
($this->getPageNum()+1).'&'.$queryvars.'">Next</a> ';
}

return $nav;
}
}

Написание класса вообще-то занимает больше времени, чем обычного смешанного кода, но если этот класс вы будете использовать и дальше в вашей работе, то выгода будет очевидной. Кроме того, одним временем все не определяется.

<<...предыдущий материал

Автор: Kevin Yank
Перевод: Михаил Дубаков
Web-анатомия

Спонсор раздела

Рассылки Subscribe.ru:

Библиотека сайтостроительства - новости, статьи, обзоры
Дискуссионный лист для web-разработчиков
Подписка на MailList.Ru
Автор: NunDesign
Другие разделы
Оптимизация сайтов
Web-студии
» Новое в разделе
Web-дизайн
Web-программирование
Интернет-реклама
Раскрутка сайта
Web-графика
Flash
Adobe Photoshop
Рассылка
Инструменты вебмастера
Контент для сайта
HTML/DHTML
Управление web-проектами
CSS
I2R-Журналы
I2R Business
I2R Web Creation
I2R Computer
рассылки библиотеки +
И2Р Программы
Всё о Windows
Программирование
Софт
Мир Linux
Галерея Попова
Каталог I2R
Партнеры
Amicus Studio
NunDesign
Горящие путевки, идеи путешествийMegaTIS.Ru

2000-2008 г.   
Все авторские права соблюдены.
Rambler's Top100