В современных проектах на WordPress часто возникает необходимость реализовать динамическую фильтрацию контента на страницах сайта. Это позволяет пользователям быстро находить нужные записи по заданным критериям, не перегружая сервер и не перегружая интерфейс. В этой статье мы подробно рассмотрим, как создать динамические фильтры в WordPress с использованием REST API и немного кастомного кода.
Почему REST API подходит для динамических фильтров
WordPress REST API — мощный инструмент, который позволяет обращаться к данным сайта через HTTP-запросы. Это значит, что можно подгружать данные асинхронно, без перезагрузки страницы, что улучшает пользовательский опыт и снижает нагрузку на сервер.
Используя REST API, мы можем создавать собственные маршруты или расширять существующие endpoint’ы для фильтрации записей по произвольным параметрам, например, категориям, таксономиям, метаданным и т.д.
В итоге мы получаем удобный и гибкий механизм фильтрации, который легко интегрируется с фронтенд-фреймворками или обычным JavaScript.
Создаем кастомный REST API endpoint для фильтрации постов
Для начала создадим собственный endpoint, который будет принимать параметры фильтрации и возвращать отфильтрованные записи. Добавьте следующий код в файл functions.php вашей темы или в отдельный плагин:
add_action('rest_api_init', function () {
register_rest_route('wpfind/v1', '/filter-posts', array(
'methods' => 'GET',
'callback' => 'wpfind_filter_posts_callback',
'permission_callback' => '__return_true',
));
});
function wpfind_filter_posts_callback(WP_REST_Request $request) {
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'paged' => $request->get_param('page') ?: 1,
);
// Фильтрация по категории
if ($category = $request->get_param('category')) {
$args['category_name'] = sanitize_text_field($category);
}
// Фильтрация по произвольному метаполю
if ($meta_key = $request->get_param('meta_key') && $meta_value = $request->get_param('meta_value')) {
$args['meta_query'] = array(
array(
'key' => sanitize_text_field($meta_key),
'value' => sanitize_text_field($meta_value),
'compare' => '=',
),
);
}
$query = new WP_Query($args);
$posts = array();
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
$posts[] = array(
'id' => get_the_ID(),
'title' => get_the_title(),
'excerpt' => get_the_excerpt(),
'link' => get_permalink(),
);
}
wp_reset_postdata();
}
return rest_ensure_response(array(
'posts' => $posts,
'total' => (int) $query->found_posts,
'pages' => (int) $query->max_num_pages,
));
}Этот код регистрирует новый маршрут /wpfind/v1/filter-posts, который позволяет передавать параметры category, meta_key, meta_value и page для пагинации. В ответе мы получим отфильтрованные записи в удобном формате JSON.
Как использовать динамические фильтры на фронтенде
Теперь, когда REST API endpoint готов, можно подключить к нему JavaScript для динамической подгрузки отфильтрованных постов. Рассмотрим простой пример с использованием Fetch API:
const filterForm = document.getElementById('filter-form');
const postsContainer = document.getElementById('posts-container');
filterForm.addEventListener('submit', function(e) {
e.preventDefault();
const category = document.getElementById('category').value;
const metaKey = document.getElementById('meta_key').value;
const metaValue = document.getElementById('meta_value').value;
let url = `/wp-json/wpfind/v1/filter-posts?`;
if(category) url += `category=${encodeURIComponent(category)}&`;
if(metaKey && metaValue) url += `meta_key=${encodeURIComponent(metaKey)}&meta_value=${encodeURIComponent(metaValue)}&`;
fetch(url)
.then(response => response.json())
.then(data => {
postsContainer.innerHTML = '';
if(data.posts.length === 0) {
postsContainer.innerHTML = '<p>Посты не найдены</p>';
return;
}
data.posts.forEach(post => {
const postElem = document.createElement('div');
postElem.innerHTML = `<h3><a href="${post.link}">${post.title}</a></h3><p>${post.excerpt}</p>`;
postsContainer.appendChild(postElem);
});
});
});HTML форма фильтров может выглядеть так:
<form id="filter-form">
<label>Категория:
<input type="text" id="category" name="category" />
</label>
<label>Meta Key:
<input type="text" id="meta_key" name="meta_key" />
</label>
<label>Meta Value:
<input type="text" id="meta_value" name="meta_value" />
</label>
<button type="submit">Фильтровать</button>
</form>
<div id="posts-container"></div>Расширение функционала фильтров: работа с таксономиями и сложными метазапросами
В реальных проектах часто требуется фильтрация по нескольким таксономиям одновременно или сложные запросы с несколькими условиями для метаданных. Рассмотрим, как расширить наш endpoint для поддержки таких сценариев.
Добавим поддержку нескольких категорий и кастомных таксономий через параметр массива. Для метазапросов — поддержим несколько условий с логическими операциями.
function wpfind_filter_posts_callback(WP_REST_Request $request) {
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'paged' => $request->get_param('page') ?: 1,
);
// Таксономии
$tax_query = array('relation' => 'AND');
if ($categories = $request->get_param('categories')) { // ожидаем массив
$tax_query[] = array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array_map('sanitize_text_field', (array) $categories),
'operator' => 'IN',
);
}
if ($custom_tax = $request->get_param('custom_tax')) {
// custom_tax ожидается в формате ['taxonomy' => 'taxonomy_slug', 'terms' => ['term1','term2']]
if (isset($custom_tax['taxonomy'], $custom_tax['terms']) && is_array($custom_tax['terms'])) {
$tax_query[] = array(
'taxonomy' => sanitize_text_field($custom_tax['taxonomy']),
'field' => 'slug',
'terms' => array_map('sanitize_text_field', $custom_tax['terms']),
'operator' => 'IN',
);
}
}
if (count($tax_query) > 1) {
$args['tax_query'] = $tax_query;
}
// Метазапрос
$meta_query = array('relation' => 'AND');
$meta_filters = $request->get_param('meta_filters');
if ($meta_filters && is_array($meta_filters)) {
foreach ($meta_filters as $filter) {
if (isset($filter['key'], $filter['value'], $filter['compare'])) {
$meta_query[] = array(
'key' => sanitize_text_field($filter['key']),
'value' => sanitize_text_field($filter['value']),
'compare' => sanitize_text_field($filter['compare']),
);
}
}
}
if (count($meta_query) > 1) {
$args['meta_query'] = $meta_query;
}
$query = new WP_Query($args);
$posts = array();
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
$posts[] = array(
'id' => get_the_ID(),
'title' => get_the_title(),
'excerpt' => get_the_excerpt(),
'link' => get_permalink(),
);
}
wp_reset_postdata();
}
return rest_ensure_response(array(
'posts' => $posts,
'total' => (int) $query->found_posts,
'pages' => (int) $query->max_num_pages,
));
}Такой подход позволит гибко комбинировать фильтры и строить сложные запросы к базе.
Практические рекомендации и полезные плагины
Для упрощения работы с REST API и фильтрами можно использовать готовые плагины. Например, Clearfy Pro имеет расширенные возможности по оптимизации и управлению REST API, что позволяет улучшить безопасность и производительность.
Еще один полезный плагин — ABC Pagination, который поможет реализовать удобную пагинацию для асинхронно подгружаемых записей.
Если вы хотите добавить визуальные фильтры и виджеты, обратите внимание на плагины с поддержкой AJAX фильтрации, например, FacetWP или JetSmartFilters (хотя они коммерческие и могут быть тяжелее для простых проектов).
Оптимизация и безопасность при работе с REST API фильтрами
При создании кастомных фильтров важно соблюдать меры безопасности. Никогда не забывайте санитизировать входящие параметры, чтобы избежать SQL-инъекций и других уязвимостей.
Также стоит ограничить количество возвращаемых записей и использовать пагинацию, чтобы не допустить перегрузки сервера.
Для повышения производительности можно кэшировать результаты запросов с помощью Transients API или внешних систем кеширования.
И, конечно, тщательно тестируйте фильтры на больших объемах данных, особенно если используются сложные метазапросы и таксономии.