Одной из частых задач в разработке на WordPress является создание удобной пагинации для кастомных типов записей (Custom Post Types, CPT) с дополнительными фильтрами. Плагин WP-PageNavi отлично справляется с задачей кастомной пагинации, однако требует правильной настройки для работы с кастомными запросами и фильтрами. В этой статье подробно разберём, как реализовать такую пагинацию, включая примеры кода и рекомендации по оптимизации.
Почему стандартная пагинация не всегда подходит для кастомных записей
Стандартная пагинация WordPress, основанная на paginate_links() или функциях типа the_posts_pagination(), отлично работает для обычных запросов на посты. Но когда речь идёт о кастомных типах записей и фильтрации по мета-данным или таксономиям, возникает необходимость создавать свои WP_Query с параметрами, а пагинация должна учитывать эти параметры.
Здесь и возникает необходимость использовать WP-PageNavi, который позволяет более гибко контролировать вывод пагинации и интегрироваться с кастомными запросами.
Основные шаги для реализации пагинации кастомных записей с фильтрами
Для начала рассмотрим классический пример: есть кастомный тип записей product и фильтр по мета-полю price_range. Нужно вывести список продуктов с пагинацией и фильтрацией.
Создание кастомного запроса с учётом пагинации
Первым делом нужно сформировать правильный WP_Query, учитывающий текущую страницу и параметры фильтра.
function wppagenavi_get_filtered_products() {
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$price_range = isset($_GET['price_range']) ? sanitize_text_field($_GET['price_range']) : '';
$meta_query = [];
if ($price_range) {
if ($price_range === 'low') {
$meta_query[] = [
'key' => 'price',
'value' => [0, 100],
'compare' => 'BETWEEN',
'type' => 'NUMERIC'
];
} elseif ($price_range === 'medium') {
$meta_query[] = [
'key' => 'price',
'value' => [101, 500],
'compare' => 'BETWEEN',
'type' => 'NUMERIC'
];
} elseif ($price_range === 'high') {
$meta_query[] = [
'key' => 'price',
'value' => 501,
'compare' => '>=',
'type' => 'NUMERIC'
];
}
}
$args = [
'post_type' => 'product',
'posts_per_page' => 10,
'paged' => $paged,
'meta_query' => $meta_query
];
return new WP_Query($args);
}
Здесь мы берём параметр paged из запроса и собираем мета-запрос для фильтрации по цене.
Вывод результатов и пагинации с WP-PageNavi
Далее нужно вывести записи и использовать WP-PageNavi для отображения пагинации.
$query = wppagenavi_get_filtered_products();
if ($query->have_posts()) :
echo '<div class="products-list">';
while ($query->have_posts()) : $query->the_post();
echo '<h2>' . get_the_title() . '</h2>';
echo '<div>Цена: ' . get_post_meta(get_the_ID(), 'price', true) . '</div>';
endwhile;
echo '</div>';
// Пагинация
echo wppagenavi();
wp_reset_postdata();
else :
echo '<p>Продукты не найдены.</p>';
endif;
Важно, чтобы функция wppagenavi() работала с текущим WP_Query. По умолчанию WP-PageNavi берёт глобальный $wp_query, поэтому если используете кастомный запрос, нужно временно заменить глобальный объект.
Подключение WP-PageNavi к кастомному запросу
Чтобы пагинация корректно работала, используем следующий приём:
global $wp_query;
$tmp_query = $wp_query;
$wp_query = $query;
wppagenavi();
$wp_query = $tmp_query;
Так мы позволяем плагину видеть параметры кастомного запроса и корректно строить ссылки пагинации.
Добавление параметров фильтра в ссылки пагинации
Очень важный момент — при фильтрации параметры $_GET должны сохраняться в URL пагинации, иначе при переходе по страницам фильтр пропадёт.
WP-PageNavi не поддерживает это из коробки, поэтому нам нужно добавить фильтр, который будет дополнять ссылки нужными параметрами.
add_filter('wp_pagenavi', 'wppagenavi_add_query_args', 10, 2);
function wppagenavi_add_query_args($html, $args) {
if (!empty($_GET)) {
$query_args = $_GET;
// Исключаем параметр paged, он подставляется отдельно
unset($query_args['paged']);
foreach ($query_args as $key => $value) {
$html = preg_replace_callback('/href="([^"]+)"/', function($matches) use ($key, $value) {
$url = $matches[1];
$url = add_query_arg($key, $value, $url);
return 'href="' . esc_url($url) . '"';
}, $html);
}
}
return $html;
}
Этот код проходится по всем ссылкам пагинации и добавляет к ним параметры фильтра из текущего запроса.
Оптимизация и кеширование результатов пагинации
При использовании кастомных запросов с фильтрами и пагинацией важно учитывать нагрузку на базу данных. Особенно если фильтров много и данных много.
Рекомендуется использовать кеширование результатов запроса, например, через Transients API или кеширование на уровне сервера (Redis, Memcached), чтобы не запускать однотипные запросы постоянно.
Пример простого кеширования с Transients API:
function wppagenavi_get_filtered_products_cached() {
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$price_range = isset($_GET['price_range']) ? sanitize_text_field($_GET['price_range']) : '';
$transient_key = 'wppagenavi_products_' . md5($paged . '_' . $price_range);
$query = get_transient($transient_key);
if (false === $query) {
$query = wppagenavi_get_filtered_products();
set_transient($transient_key, $query, 3600); // кеш на час
}
return $query;
}
Замените вызов функции в шаблоне на wppagenavi_get_filtered_products_cached(), чтобы снизить нагрузку.
Поддержка AJAX пагинации для кастомных записей с фильтрами
Для улучшения UX можно реализовать AJAX пагинацию, чтобы при смене страницы или фильтра не перезагружать всю страницу.
Для этого понадобится JavaScript-код, который будет отправлять AJAX-запрос с параметрами фильтра и страницы, и PHP-обработчик, который вернёт HTML списка и пагинации.
Пример простого AJAX обработчика:
add_action('wp_ajax_wppagenavi_filter', 'wppagenavi_ajax_filter_handler');
add_action('wp_ajax_nopriv_wppagenavi_filter', 'wppagenavi_ajax_filter_handler');
function wppagenavi_ajax_filter_handler() {
$paged = isset($_POST['paged']) ? intval($_POST['paged']) : 1;
$price_range = isset($_POST['price_range']) ? sanitize_text_field($_POST['price_range']) : '';
// Формируем запрос
$_GET['paged'] = $paged;
$_GET['price_range'] = $price_range;
$query = wppagenavi_get_filtered_products();
ob_start();
if ($query->have_posts()) {
while ($query->have_posts()) : $query->the_post();
echo '<h2>' . get_the_title() . '</h2>';
endwhile;
global $wp_query;
$tmp_query = $wp_query;
$wp_query = $query;
wppagenavi();
$wp_query = $tmp_query;
} else {
echo '<p>Продукты не найдены.</p>';
}
wp_reset_postdata();
$response = ob_get_clean();
wp_send_json_success($response);
}
Клиентский JS должен отправлять AJAX-запрос на admin-ajax.php с параметрами фильтра и страницы, и обновлять содержимое списка и пагинации.
Выводы и рекомендации
- Используйте WP-PageNavi с кастомными запросами, временно заменяя глобальный
$wp_query. - Обязательно сохраняйте параметры фильтров в ссылках пагинации с помощью фильтра
wp_pagenavi. - Реализуйте кеширование запросов для снижения нагрузки на базу.
- Для лучшего UX добавьте AJAX пагинацию с обновлением контента без перезагрузки страницы.
- Проверяйте и фильтруйте входящие параметры, чтобы избежать ошибок и уязвимостей.
Если хотите расширить функционал, обратите внимание на плагин WP-PageNavi Pro с дополнительными настройками и поддержкой AJAX.