Фильтрация записей в WordPress — частая задача, особенно если нужно вывести уникальный набор постов с определёнными параметрами, без дублирования и с возможностью динамического обновления контента. В этой статье рассмотрим, как создать фильтрованный список постов с помощью класса WP_Query, а также реализовать динамическую подгрузку через AJAX, чтобы пользователи могли менять параметры фильтра без перезагрузки страницы.
Понимание WP_Query для фильтрации записей
WP_Query — основной инструмент для выборки постов по заданным условиям. Его возможности позволяют настраивать запросы по таксономиям, метаданным, дате, статусу и многому другому. Но чтобы получить уникальный список (без повторений), нужно правильно управлять параметром post__not_in, чтобы исключать уже выведенные ID постов.
Рассмотрим пример запроса, который выводит 10 постов типа post, исключая уже показанные записи:
$args = [
'post_type' => 'post',
'posts_per_page' => 10,
'post__not_in' => $already_shown_ids,
'orderby' => 'date',
'order' => 'DESC'
];
$query = new WP_Query($args);Здесь $already_shown_ids — массив ID постов, которые мы уже вывели, чтобы избежать дублирования.
Как сформировать массив исключаемых ID
Если вы хотите создавать многостраничный фильтрованный список, где на каждой следующей странице не выводятся посты из предыдущих, то нужно хранить массив этих ID в JavaScript и передавать на сервер через AJAX-запрос.
Для начала, после первого вывода записей, вы можете собрать ID так:
$already_shown_ids = wp_list_pluck( $query->posts, 'ID' );Дальше этот массив передать на клиент и использовать при следующих запросах.
Реализация AJAX фильтрации
Чтобы динамично обновлять список постов без перезагрузки страницы, используем AJAX. Для этого добавим JavaScript-код и обработчик на PHP.
JavaScript: отправка AJAX-запроса
Пример скрипта, который отправляет запрос с параметрами фильтра и исключаемыми ID:
jQuery(document).ready(function($) {
var excludedIDs = [];
function loadFilteredPosts(filters) {
$.ajax({
url: wpbono_ajax_object.ajax_url,
method: 'POST',
data: {
action: 'wpbono_load_filtered_posts',
filters: filters,
exclude: excludedIDs
},
success: function(response) {
if(response.success) {
$('#post-list').html(response.data.html);
excludedIDs = response.data.excluded_ids;
} else {
alert('Ошибка загрузки постов');
}
}
});
}
// Пример вызова
$('#filter-button').on('click', function() {
var filters = {
category: $('#category-select').val(),
tag: $('#tag-select').val()
};
loadFilteredPosts(filters);
});
});PHP: обработчик AJAX-запроса
В functions.php или подключаемом файле добавьте следующий код:
add_action('wp_ajax_wpbono_load_filtered_posts', 'wpbono_load_filtered_posts_callback');
add_action('wp_ajax_nopriv_wpbono_load_filtered_posts', 'wpbono_load_filtered_posts_callback');
function wpbono_load_filtered_posts_callback() {
$filters = isset($_POST['filters']) ? (array) $_POST['filters'] : [];
$exclude = isset($_POST['exclude']) ? array_map('intval', $_POST['exclude']) : [];
$args = [
'post_type' => 'post',
'posts_per_page' => 10,
'post__not_in' => $exclude,
'orderby' => 'date',
'order' => 'DESC'
];
if (!empty($filters['category'])) {
$args['category_name'] = sanitize_text_field($filters['category']);
}
if (!empty($filters['tag'])) {
$args['tag'] = sanitize_text_field($filters['tag']);
}
$query = new WP_Query($args);
ob_start();
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
?><div class="post-item">
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<div><?php the_excerpt(); ?></div>
</div><?php
}
} else {
echo '<p>Посты не найдены</p>';
}
wp_reset_postdata();
$html = ob_get_clean();
$new_exclude = array_merge($exclude, wp_list_pluck($query->posts, 'ID'));
wp_send_json_success(['html' => $html, 'excluded_ids' => $new_exclude]);
}Обратите внимание, что в ответе мы возвращаем обновлённый список исключаемых ID, чтобы следующий запрос учитывал уже показанные посты.
Оптимизация и кеширование запросов
При динамической загрузке большого количества данных важно не перегружать базу данных. Рекомендуется кешировать результаты с помощью Transients API или использовать плагины кеширования, например Clearfy Pro, который позволяет оптимизировать запросы и управлять кешем.
Также для сложных фильтров можно использовать плагины, которые расширяют возможности WP_Query, например, WPRemark для расширенной фильтрации и поиска.
Поддержка пользовательских типов записей и таксономий
Если у вас на сайте используются кастомные типы записей (Custom Post Types) и таксономии, то фильтрацию можно расширить, добавив параметры в WP_Query:
$args = [
'post_type' => 'product',
'tax_query' => [
[
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => 'electronics'
]
],
'posts_per_page' => 10,
'post__not_in' => $exclude_ids
];Это позволит выводить уникальный список товаров из категории «electronics», исключая уже показанные элементы.
Пример расширенного AJAX-запроса с CPT и таксономиями
В AJAX-обработчике добавьте логику фильтрации по CPT и таксономиям, передавая их из JavaScript.
Использование WPBonо хука для кастомной логики
Для расширения функционала можно использовать собственные хуки, например wpbono_pre_filter_query, чтобы динамически менять аргументы запроса перед выполнением:
add_filter('wpbono_pre_filter_query', function($args, $filters) {
if (!empty($filters['custom_flag'])) {
$args['meta_query'][] = [
'key' => 'custom_flag',
'value' => 'yes'
];
}
return $args;
}, 10, 2);Это позволит гибко управлять фильтрами без изменения основного кода.
Итог
Создание уникального фильтрованного списка постов с помощью WP_Query и AJAX — мощный способ улучшить UX вашего сайта на WordPress. Такой подход позволяет пользователю быстро находить нужный контент, при этом исключая дублирование и оптимизируя нагрузку на сервер. Использование собственных хуков и плагинов с расширенными возможностями сделает вашу фильтрацию ещё гибче и удобнее.