Что такое «следующая страница» и «предыдущая страница» при просмотре списка, например, каких-нибудь товаров? Ясно, что определение соседей будет зависеть от сортировки, от разреза, по которому выбираются товары, и даже от слов поиска (если поиск предусмотрен системой).

Ссылки next и previous на странице

Всем нам с детства знакомы эти ссылки – хотя бы по страницам документации PHP:

phpdoc1.png

Мы находимся на странице описания функции array_slice, а вверху страницы видим ссылки на соседние темы: array_shift слева (предыдущая) и array_splice справа (следующая). Как организовать такую фигню для произвольного списка (например, для просмотра списка товаров)?

Очевидным кажется примерно такое решение (1). Исходим из того, что данные хранятся в БД Mysql, а первичный ключ (поле id) – возрастающие числа (auto_increment). Идентификатор текущего элемента (страницы, на которой находимся) мы знаем (current_id = 3), делаем в процессе генерации страницы ещё один запрос к базе: select * from `tovar` where `id` = (current_id – 1) – и находим предыдущий элемент. Таким же запросом, но с `id` = current_id + 1, находим следующий элемент. И рисуем ссылки вида href="static/tovar.php?id=2".

Но если позже мы удалим товар с id=4, то по вычисленной методом 1 ссылке попадём на пустую страницу (404). Отсюда возникает Решение 2: делаем запрос не "напрямую" к вычисленному в PHP элементу, а по "относительному пути": select * from `tovar` where `id` < current_id limit 1 (и такой же запрос, но с `id` > current_id, – для следующего элемента).

Сортировка по имени

Метод Решения 2 работает практически идеально, пока мы не захотим изменить сортировку начального списка. Например, удобно бывает просматривать товары, упорядоченные по наименованию (а вовсе не по абстрактным числам-идентификаторам). Хотя... что мешает написать в запросе where `name` < 'current_name'? Видимо то, что в базе может быть два товара с абсолютно одинаковым наименованием, а отличия могут храниться, например, в поле "спецификация".

Поиск по произвольным словам

Пользователь может просматривать список результатов поиска по всей базе товаров, или по определённой рубрике, отсортированный по наименованиям или по дате изменения... В принципе, в любом случае можно сформировать sql-запрос с учётом всех входящих условий отбора и сортировки. Но зачем? Почему мы должны заново конструировать сложный запрос при получении отдельных элементов (next & previous) списка, если мы один раз его уже сконструировали – при получении всего начального списка?

Хранение начального списка (Решение 3)

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

Массивы эти хранить, пожалуй, можно только в сессии, например, в свойствах $_SESSION['list_ids'] и $_SESSION['list_names']. Тогда ссылку на соседний элемент можно будет формировать примерно так:

Критика хранимого списка

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

Отсюда вытекает «пуленепробиваемый» способ получения соседних элементов – повторное обращение на странице элемента к функционалу, формирующему начальный список. Казалось бы, это сложнее, чем «лишний» запрос к базе. Да, наверное, немного сложнее (больше работы) – для компьютера. Но зато вдвое меньше работы для программиста! Мы ведь не добавляем новые функции, а повторно используем готовые.

Получение списка динамически (Решение 4)

Допустим, мы формируем видимый список товаров с помощью вызова $catalog->get($params). Тогда при листании товаров нужно будет перед выводом каждой страницы получать тот же список с теми же параметрами. Откуда же их взять, «те же параметры», если мы уже ушли со страницы списка? Изначально они брались из УРЛ, значит, всё-таки надо где-то хранить УРЛ страницы списка. То есть уже не сам список, а как бы «ключ» к нему.

В УРЛ списка у нас есть всё необходимое, включая строку поиска: "tovar_list.php?category=4&q=люминивые". С одной стороны, удобно, а с другой – придётся запускать весь движок, чтобы начать с разбора УРЛ и закончить получением списка. Поэтому, для обеспечения «короткого вызова» вида $catalog->get($params) прямо где-нибудь в шаблоне, лучше всё-таки хранить в сессии конкретно вот эти $params, а не УРЛ. В общем, схема получается такая:

  1. При каждом обращении к странице списка с помощью заклинания $catalog->get($params), записываем в сессию: $_SESSION['tovar_list_params'] = $params;.
  2. При открытии страницы элемента получаем $params = $_SESSION['tovar_list_params'], и затем получаем заново весь список с помощью того же $catalog->get($params).
  3. Там, в полученном списке (или в самом объекте $catalog) есть два нужных нам массива $list_ids и $list_names (о чём мы заблаговременно позаботились в коде класса Catalog); с их помощью мы уже находим соседние элементы, как в Решении 3.
  4. Можно даже не запускать полностью весь функционал Каталога, если, например, кэшировать массивы $list_ids и $list_names в файлах, и получать их при вызовах Каталога прямо из файлов, не производя повторных вычислений в коде. Разумеется весь кэш надо будет уничтожать при любых изменениях таблицы Товаров.
Дилетант

Комментарии