Новый тип навигационной системы при постраничном выводе (Felenka)
Оглавление
Введение
(Вернуться к оглавлению)
Практически каждый разработчик сталкивался с необходимостью постраничного вывода информации. Каким бы ни был проект, в нем всегда есть "что-то", что не помещается на одну страницу: авторские стихи в маленькой домашней страничке или архив новостей в информационном портале. И каждый раз, когда мы создаем механизм поиска "Первой страницы", "Следующей страницы", "Предыдущей страницы", мы занимаемся рутинной работой. Каждый раз, когда мы в очередной раз переделываем готовую функцию постраничного вывода под новый интерфейс, мы напрасно теряем время. Однако, как правило, попытки создать какую-то единую универсальную функцию постраничного вывода наталкивались на то, что постраничный вывод тесно связан с контекстом. Эту связь можно разделить по степени важности на два уровня:
Проблема взаимозависимости контекста и постраничного вывода (основная проблема). Каждый раз, когда мы переходим от одной страницы к другой, мы должны "тащить" за собой огромное количество дополнительных данных, полученных из контекста: например, если мы листаем результаты поиска, то неплохо было бы сохранить параметры поиска при переходах от страницы к странице. Но эта зависимость обоюдная, ведь контекст, в свою очередь, зависит от параметров постраничного вывода: очевидно, что когда мы выбираем просмотр архива новостей за июль, мы будем удивлены, если встретим там новогодние поздравления.
Проблема, связанная с организацией внешнего вида (второстепенная проблема). В каждом проекте свой метод вывода информации. Это касается не только внешнего вида постраничных переходов (это может быть кнопка, которая переходит от страницы к странице с помощью метода POST; это может быть ссылка, которая формирует GET-овую строку запроса; и др.), но и способа хранения данных (кнопка или ссылка может формироваться динамически, "на лету"; это может быть шаблон, хранящийся в БД, и др.)
Невозможно создать универсальную систему постраничного вывода (читай: независимую от контекста), если невозможно разделить постраничный вывод и контекст. Причем не заниматься этим "разделением" в каждом проекте, а сделать это один раз, создав универсальную систему, и затем просто использовать ее. Всё это приводит нас к следующему разделу:
Цели и задачи
(Вернуться к оглавлению)
Целью этой статьи является рассказать об общем методе решения основной проблемы. Это решение неполно, но оно оставлено таким намеренно, чтобы не ограничивать свободы творчества тех разработчиков, которые решат воспользоваться им. Второстепенная проблема практически не затронута, но если идея, реализованная в этой статье, вызовет интерес, то, естественно, мы еще вернемся к этому вопросу.
Однако, прежде, чем начинать обсуждение чего-либо, хотелось бы "сверить часы": объяснить значение терминов, используемых в этой статье, создать своеобразный терминологический словарь:
Словарь терминов
(Вернуться к оглавлению)
Термины в словаре введены в логическом порядке: если термин содержит другие термины, то эти термины были определены раньше.
Постраничный просмотр - способ вывода запрошенной пользователем информации по частям. Каждый постраничный просмотр имеет собственные параметры деления информации на части (например, можно выводить информацию о каждой неделе или о каждом дне текущего месяца). При постраничном просмотре может быть видима информация только одной выбранной части. Эти части называются "страницами".
Страница - часть информации, выводимая на при постраничном просмотре, в соответствии с заданными параметрами деления информации на страницы.
Навигационная система или система постраничного вывода - это набор кнопок или ссылок, позволяющих пользователю осуществить постраничный просмотр (см. пример ?1).
Пример 1. Простейшая неграфическая навигационная система
Первая страница |
Предыдущая страница |
4 |
5 |
6 |
7 |
8 |
9 |
Следующая страница |
Последняя страница |
Активная страница - страница, содержимое которой в данный момент просматривает пользователь. В примере ?1 активной является седьмая (7) страница.
Навигационная ссылка - кнопка или ссылка (в зависимости от реализации), с помощью которой пользователь может "пролистать" на ту страницу, которая в данный момент не является активной. В примере ?1 страницы 4-6, 8-9, "Первая страница", "Предыдущая страница", "Следующая страница", "Последняя страница" являются навигационными ссылками.
Строка в формате md5 - строка, содержащая параметры системы постраничного вывода, однако сформированная таким образом, чтобы внешне походить на md5-строки, генерируемые функцией md5(). См. пример ?2.
Основная идея
(Вернуться к оглавлению)
Основная идея, изложенная в этой статье, проста: если очень сложно (практически невозможно) отделить контекст от параметров постраничного вывода, то нужно попробовать использовать другой внешний вид этих параметров при передаче их в навигационных ссылках. Как правило, все системы постраничного вывода обладают несколькими основными параметрами, а именно:
- Количество элементов
- Номер активной страницы
- Количество навигационных ссылок
- Количество элементов на активной странице
Для того, чтобы постраничный вывод был корректным, каждый из этих параметров должен быть целым неотрицательным числом. Именно этот факт натолкнул меня на идею: передавать все параметры постраничного вывода внутри одного, специально отформатированного, параметра. В этой статье мы будем рассматривать упрощенную систему постраничного вывода, в которой отслеживаются только два основных параметра:
- Количество элементов
- Номер активной страницы
Тому есть 2 причины: во-первых, наши рассуждения могут быть легко распространены на два других параметра; во-вторых, это не будет отвлекать нас в процессе рассуждений.
Теперь изложим нашу идею более подробно для системы постраничного вывода с двумя основными параметрами: поскольку оба параметра - количество элементов и номер активной страницы - являются целыми неотрицательными числами, наиболее оптимальные решением было бы передавать их в виде специально сформированной строки, например, abcd2as15ks2sdm3 (См. пример ?2)
Пример 2. Общий вид специально отформатированной строки, объединяющей оба основных параметра системы постраничного вывода
abcd |
2 |
as |
15 |
ks |
2sdm3 |
↑ |
↑ |
↑ |
↑ |
↑ |
↑ |
"Маскирующая" часть 1 |
Номер активной страницы |
"Разделяющая" часть 2 |
Общее число элементов |
"Разделяющая" часть 3 |
"Маскирующая" часть 4 |
↑ |
↑ |
↑ |
↑ |
↑ |
↑ |
Необязательная часть. Мы сами определим, как она должна выглядеть.
|
Целое число ≥ 0
|
Разделитель-"не цифра" (например, буквы латинского алфавита)
|
Целое число ≥ 0
|
Разделитель-"не цифра" (например, буквы латинского алфавита)
|
Необязательная часть. Мы сами определим, как она должна выглядеть.
|
Создание строки в формате md5
(Вернуться к оглавлению)
Итак, давайте подробней обсудим, как будет формироваться наша cтрока в формате md5. Конечно, можно использовать любой другой алгоритм ее формирования, и необязательна эта строка должна быть похожа на md5-строку. И, конечно же, наш способ приведен исключительно в качестве примера. Мы предлагаем формировать строку в формате md5 с учетом следующих условий:
Будем использовать "маскирующую" часть 1 фиксированной длины в 5 символов; для этого сгенерируем с помощью функции md5() строку и будем использовать ее первые 5 символов:
define('PART1_LENGTH', 5);
//...
srand(time());
$part_1 = substr(md5(uniqid(mt_rand())), 0, PART1_LENGTH);
Будем использовать "разделяющую" часть 2 фиксированной длины в 3 символа, который будем случайным образом выбирать из заранее определенной строки $t_str :
define('PART2_LENGTH', 3);
//...
srand(time());
$t_str = eregi_replace('[0-9]', '', md5(uniqid(mt_rand())));
$part_2 = substr($t_str, 0, PART2_LENGTH);
Аналогично "разделяющей" части 2, найдем "разделяющую" часть 3:
define('PART3_LENGTH', 2);
//...
srand(time());
$t_str = eregi_replace('[0-9]', '', md5(uniqid(mt_rand())));
$part_3 = substr($t_str, 0, PART3_LENGTH);
Теперь совместим все части вместе. Получим результирующую строку вида:
$res = $part_1.$curindex.$part_2.$total.$part_3;
где
- $part_1 - "маскирующая" часть 1, полученная на шаге 1;
- $curindex - номер текущей страницы;
- $part_2 - "разделяющая" часть 2, полученная на шаге 2;
- $total - общее число элементов
- $part_3 - "разделяющая" часть 3, полученная на шаге 3.
Длина нашей строки $res , полученной на шаге 4, может быть меньше, чем 32 символа, и поэтому, поскольку мы создаем строку в формате md5, сгенерируем с помощью функции md5() еще одну, "дополняющую" строку и будем использовать ее для "дополнения" до 32-х символов нашей строки $res . Для этого воспользуемся функцией str_pad():
define('GENERAL_LENGTH', 32);
//...
srand(time());
$res = str_pad($res, GENERAL_LENGTH, md5(uniqid(mt_rand())), STR_PAD_RIGHT);
где функция str_pad() принимает следующие параметры (более подробно - см. описание функции на сайте http://www.php.net):
- $res - результирующая строка, полученная на шаге 4;
- GENERAL_LENGTH - длина строки, которая будет сравниваться с длиной строки
$res ; если длина строки $res меньше, чем GENERAL_LENGTH, то наша строка $res будет дополнена в соответствии с последующими двумя параметрами до длины GENERAL_LENGTH;
- md5(uniqid(mt_rand())) - случайным образом сгенерированная строка в формате md5, которая будет использоваться до "дополнения" нашей строки
$res до длины GENERAL_LENGTH;
- STR_PAD_RIGHT - означает, что "дополняющая" строка будет добавляться к нашей строке
$res справа.
Оформим формирование строки в формате md5 в виде отдельной функции:
define('PART1_LENGTH', 5);
define('PART2_LENGTH', 3);
define('PART3_LENGTH', 2);
define('GENERAL_LENGTH', 32);
//...
function makeSecureParam($curindex, $total){
srand(time());
$part1 = substr(md5(uniqid(mt_rand())), 0, PART1_LENGTH);
srand(time());
$t_str = eregi_replace('[0-9]', '', md5(uniqid(mt_rand())));
$part2 = substr($t_str, 0, PART2_LENGTH);
srand(time());
$t_str = eregi_replace('[0-9]', '', md5(uniqid(mt_rand())));
$part3 = substr($t_str, 0, PART3_LENGTH);
srand(time());
return str_pad($part1.$curindex.$part2.$total.$part3,
GENERAL_LENGTH,
md5(uniqid(mt_rand())),
STR_PAD_RIGHT );
}
Итак, основная идея нами реализована : мы создали функцию, позволяющую "прятать" параметры системы постраничного вывода внутрь одной, специального вида строки. Какие преимущества нам это дает? Поскольку теперь наша система постраничного вывода характеризуется всего одним параметром, то это даст нам возможность встраивать систему постраничного вывода в контекст, а не наоборот, как это было раньше. Осталось только реализовать класс, который позволил бы принимать эту специального вида строку и преобразовывать ее в соответствующие параметры навигационной системы.
Описание класса
(Вернуться к оглавлению)
Мы создадим базовый класс, в котором присутствуют всего 2 функции: конструктор и инициализация основных параметров системы постраничного вывода по заранее данной строке специального формата.
Мы используем именно класс, хоть это и несколько замедлит выполнение скрипта, для того чтобы иметь возможность использовать несколько систем постраничного вывода одновременно (например, постраничный вывод новостей еженедельно и постраничный вывод новостей ежедневно внутри каждой выбранной недели) без каких-либо особых осложнений. Описание класса приведено ниже:
Класс содержит две переменных: общее число элементов и номер активной страницы.
class navigation{
var $curpage; /* active page number */
var $total; /* total items */
//...
}
Конструктор класса инициализирует "пустой" набор элементов (это предоставит возможность инициализировать экземпляр класса несколько раз):
class navigation{
//...
function navigation(){
$this->curpage = $this->total = 0;
}
}
Прежде, чем анализировать заданную строку в формате md5, исключим из нее "маскирующую" часть 1:
$str = substr($str, PART1_LENGTH);
Проанализируем заданную строку в формате md5 с помощью функции preg_split() (более подробно - см. описание функции на сайте http://www.php.net):
preg_split('/[^0-9]/si', $str, 3, PREG_SPLIT_NO_EMPTY)
где
- '/[^0-9]/si' - образец, позволяющий выделить из строки
$str те ее части, которые состоят только из цифр; например, если
$str = 'cf16b2bcf12fd72dc05154e87749d940'
то результатом выполнения функции
preg_split('/[^0-9]/si', $str)
будет массив значений:
- 16
- 2
- 12
- 72
- 05154
- 87749
- 940
$str - заданная строка в формате md5;
- 3 - специальный параметр функции preg_split(), которая говорит, что нужно анализировать только первых три (3) совпадения в заданной строке
$str ;например, если
$str = 'cf16b2bcf12fd72dc05154e87749d940'
то результатом выполнения функции
preg_split('/[^0-9]/si', $str, 3)
будет массив значений:
- 16
- 2
- cf12fd72dc05154e87749d940
PREG_SPLIT_NO_EMPTY - специальный параметр, который позволяет выводить только непустые строки, которые соответствуют заданному шаблону поиска.
Поскольку функция preg_split() возвращает в качестве результата массив значений, то наиболее удобно использовать функцию list() обработки результатов - запишем первое полученное число во временную переменную $curpage , а второе - во временную переменную $total (этот порядок определяется способом формирования строки в формате md5 с помощью функции "makeSecureParam "):
@list($curpage, $total) = preg_split('/[^0-9]/si', $str, 3, PREG_SPLIT_NO_EMPTY);
Чтобы исключить всевозможные "неожиданности", преобразуем полученные результаты к целым неотрицательным числам, а результат запишем в соответствующие переменные нашего класса:
$this->curpage = abs(intval($curpage));
$this->total = abs(intval($total));
Оформим анализ строки в формате md5 в виде функции init , которая позволит выделить из данной строки необходимые параметры навигационной системы: общее число элементов и номер активной страницы.
define('PART1_LENGTH', 5);
//...
class navigation{
//...
function init($str = ''){
$str = substr($str, PART1_LENGTH);
if (!trim($str)){
$this->curpage = $this->total = 0;
return;
}
@list($curpage, $total) = preg_split('/[^0-9]/si',
$str, 3, PREG_SPLIT_NO_EMPTY);
$this->curpage = abs(intval($curpage));
$this->total = abs(intval($total));
}
}
Итоги
(Вернуться к оглавлению)
Статья может быть полезной любому человеку, знакомому с синтаксисом РНР и владеющему по крайней мере начальным опытом программирования на РНР. В этой статье нет готовых рецептов. В ней изложен метод решения, которым каждый может воспользоваться. Главной идеей является "сокрытие" всех параметров навигационной системой внутри одной строки специального формата (в приведенном примере используется формат "а-ля md5" :) ). Представлена функция, формирующая строку в формате md5, и базовый класс, позволяющий инициализировать параметры навигационной системы по заданной строке в формате md5. Преимущества данного метода:
- Упрощение передачи параметров постраничного вывода в навигационных ссылках.
- Возможность использовать несколько систем постраничного вывода одновременно.
- Минимизация зависимости системы постраничного вывода от контекста.
- Возможность развития различных реализаций метода в различных направлениях.
- Простота использования в распределенных проектах, в которых участвуют несколько разработчиков.
- Возможность создания универсальных библиотечных функций.
Полностью работающий текст класса и функции, описанных в этой статье, можно скачать здесь.
Felenka
Работа с XML. SAX, DOM - краткое введение.
Библиотека обработки HTML-текста из PHP-скриптов
|