Вопросы о реализации постраничной навигации всплывают давольно часто, и не только среди новичков. Новички ищут любые “рабочие” способы, а профессионалы
пытаются оптимизировать избранные когда-либо способы. Необходимость разбивки большого количества информации встречается на каждом шагу, и сегодня я бы хотел
предоставить на суд самый оптимальный, на мой взгляд, способ суть которого лежит в возможности переложить большую часть данной работы на MySQL. Целью
исследования сегодня будет параметр SQL_CALC_FOUND_ROWS оператора SELECT. Данный параметр появился в MySQL начиная с версии 4.0.0, суть его работы
заключается в том, что при его использовании MySQL проведет “подсчет” и запись количества записей по указанному запросу, результат которого можно будет позже
получить через параметр FOUND_ROWS(), того же оператора SELECT.
Простейший пример его работы ниже:
SELECT SQL_CALC_FOUND_ROWS * FROM `table`
Если предположить, что в таблице table находится 1050 записей, то результат оператора SELECT с параметром FOUND_ROWS():
SELECT FOUND_ROWS()
будет следующий: Array ( [FOUND_ROWS()] => 1050 )
таким образом, выполняя любой SELECT запрос, с данным параметром, мы автоматически получаем возможность “отслеживать” количество записей удовлетворяющим
данному запросу, что несомненно можно использовать для реализации нашей задачи. Еще один пример. Допустим, у нас есть таблица catalogue, из которой
необходимо получать данные разбивая их на страницы. Причем обрабатывать нужно только те записи которые удовлетворяют каким либо условиям. например WHERE
`price` < 1000.00. Итого получаем следущий запрос:
SELECT * FROM `catalogue` WHERE `price` < 1000.00
Для того чтобы ограничить вывод записей в рамках одной страницы необходисо указать параметр LIMIT первое значение которого будет указывать на номер записи с
которой начинается вывод, а второе значение на количество выводимых записей и для подсчета общего количества, конечно же будем использовать указанный выше
параметр SQL_CALC_FOUND_ROWS.
Итого понимаем, что для этого нам необходим примерно следующий запрос:
SELECT SQL_CALC_FOUND_ROWS * FROM `catalogue` WHERE `price` < 1000.00 LIMIT 1, 10
Который выведет 10 записей начиная с первой из таблицы catalogue, где цена товара меньше 1000.00. Учитывая, что все программисты чертовски ленивые
мы
перекладываем ответственность за обработку стандарных запросов на свою функцию, чтобы мы могли не отвлекаясь на все "дополнительные параметры" использовать в
работе только оригинальные запросы. Соответственно перво-наперво нам необходимо получить количество записей по исходному запросу, для чего используем способ
описанный выше. Преобразуем SELECT запрос, и получаем количество записей:
$result = mysql_query('SELECT SQL_CALC_FOUND_ROWS' . substr($sql, 6));
$result = mysql_fetch_assoc(mysql_query('SELECT FOUND_ROWS()'));
Первая строка добавляет к оператору SELECT параметр SQL_CALC_FOUND_ROWS в исходной SQL строке, а вторая выполняет SQL запрос на получение количества записей.
Далее нам необходимо определить количество записей выводимых на одной странице и собственно номер текущей страницы. Для этого введем переменную $onPage со
значением 10 (10 строк на страницу), и переменную $currentPage значение которой будем брать из GET параметра "page". Подсчитаем общее количество страниц и
обработаем значение $currentPage.
$totalPages = intval(($result['FOUND_ROWS()'] - 1) / $onPage) + 1;
$currentPage = empty($currentPage) || $currentPage < 0 ? 1 : intval($currentPage);
if ($currentPage > $totalPages) $currentPage = $totalPages;
Далее нам осталось только посчитать номер записи с которой нужно начинать выборку исходя из текущей страницы, и выполнить данный запрос:
$start = $currentPage * $onPage - $onPage;
$result = db::query($sql . ' LIMIT '.$start.', '.$onPage);
В результате мы получаем ресурс в переменной $result, который уже можем обрабатывать PHP, например, через mysqli_fetch_assoc()
Чтобы еще больше упростить задачу оформим всё вышеизложенное в виде функции:
function paging ($sql, $currentPage = 1, $onPage = 10) {
$result = mysql_query('SELECT SQL_CALC_FOUND_ROWS' . substr($sql, 6));
$result = mysql_fetch_assoc(mysql_query('SELECT FOUND_ROWS()'));
$totalPages = intval(($result['FOUND_ROWS()'] - 1) / $onPage) + 1;
$currentPage = empty($currentPage) || $currentPage < 0 ? 1 : intval($currentPage);
if ($currentPage > $totalPages) $currentPage = $totalPages;
$start = $currentPage * $onPage - $onPage;
$result = mysql_query($sql . ' LIMIT '.$start.', '.$onPage);
return $result;
}
В полевых условиях используется эта функция вместо функции mysql_query.
$curPage = !isset($_GET['page']) || empty($_GET['page']) ? 1 : $_GET['page'];
$result = paging ($sql, $curPage);
while ($row = mysql_fetch_assoc($result)) {
/* ... */
}