В стандартном WP REST API фильтрация записей по метаполям (post meta) не поддерживается напрямую. Но часто в проектах возникает необходимость получать записи с определёнными значениями метаданных, например, по цене товара, статусу или дополнительным параметрам. В этой статье подробно разберём, как добавить поддержку фильтрации по метаполям в WP REST API, расширяя стандартные эндпоинты и создавая собственные.
Почему фильтрация по метаполям в WP REST API важна
Метаполя — это мощный инструмент для хранения кастомных данных, которые не помещаются в стандартные поля записи. Без возможности фильтрации по ним REST API теряет большую часть гибкости при получении нужной информации. Стандартные параметры запроса позволяют фильтровать только по базовым полям (ID, дата, статус и т.д.), но не по пользовательским метаполям.
Реализация фильтрации по метаполям позволит вам:
- Получать только релевантные записи с нужными значениями дополнительной информации.
- Оптимизировать клиентские приложения, уменьшив объём данных и количество запросов.
- Создавать более сложные и точные запросы для построения интерфейсов.
Расширение стандартного эндпоинта posts для фильтрации по метаполям
Рассмотрим пример добавления параметра meta_key и meta_value в стандартный эндпоинт /wp-json/wp/v2/posts. Для этого используем хук rest_post_query, который позволяет модифицировать WP_Query аргументы.
Добавление фильтрации по одному метаполю
add_filter('rest_post_query', 'wpapi_rest_filter_posts_by_meta', 10, 2);
function wpapi_rest_filter_posts_by_meta($args, $request) {
$meta_key = $request->get_param('meta_key');
$meta_value = $request->get_param('meta_value');
if ($meta_key && $meta_value) {
$args['meta_query'] = [
[
'key' => sanitize_text_field($meta_key),
'value' => sanitize_text_field($meta_value),
'compare' => '='
]
];
}
return $args;
}
Теперь, сделав запрос /wp-json/wp/v2/posts?meta_key=price&meta_value=100, мы получим все записи, у которых метаполе price равно 100.
Фильтрация по нескольким метаполям
Чтобы расширить функционал, добавим параметр meta_query в формате JSON, позволяющий задавать несколько условий. Это требует парсинга и валидации.
add_filter('rest_post_query', 'wpapi_rest_filter_posts_by_meta_multiple', 10, 2);
function wpapi_rest_filter_posts_by_meta_multiple($args, $request) {
$meta_query_json = $request->get_param('meta_query');
if ($meta_query_json) {
$meta_query = json_decode($meta_query_json, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($meta_query)) {
$safe_meta_query = [];
foreach ($meta_query as $query) {
if (isset($query['key'], $query['value'])) {
$safe_meta_query[] = [
'key' => sanitize_text_field($query['key']),
'value' => sanitize_text_field($query['value']),
'compare' => isset($query['compare']) ? sanitize_text_field($query['compare']) : '='
];
}
}
if ($safe_meta_query) {
$args['meta_query'] = $safe_meta_query;
}
}
}
return $args;
}
Пример запроса:
/wp-json/wp/v2/posts?meta_query=[{"key":"price","value":"100","compare":">="},{"key":"color","value":"red"}]
Этот запрос вернёт записи, где цена больше либо равна 100, и цвет — красный.
Создание отдельного кастомного эндпоинта с расширенной фильтрацией
Если нужна более гибкая логика, лучше создать собственный REST API маршрут. Это позволит полностью контролировать параметры, сортировку и формат ответа.
Регистрация кастомного эндпоинта
add_action('rest_api_init', function () {
register_rest_route('wpapi/v1', '/posts-filter/', [
'methods' => 'GET',
'callback' => 'wpapi_get_filtered_posts',
'permission_callback' => '__return_true',
'args' => [
'meta_query' => [
'required' => false,
'validate_callback' => function ($param, $request, $key) {
return is_string($param);
}
],
'per_page' => [
'required' => false,
'default' => 10,
'sanitize_callback' => 'absint'
],
'page' => [
'required' => false,
'default' => 1,
'sanitize_callback' => 'absint'
]
]
]);
});
function wpapi_get_filtered_posts(WP_REST_Request $request) {
$meta_query_json = $request->get_param('meta_query');
$per_page = $request->get_param('per_page');
$page = $request->get_param('page');
$args = [
'post_type' => 'post',
'posts_per_page' => $per_page,
'paged' => $page,
];
if ($meta_query_json) {
$meta_query = json_decode($meta_query_json, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($meta_query)) {
$args['meta_query'] = [];
foreach ($meta_query as $query) {
if (isset($query['key'], $query['value'])) {
$args['meta_query'][] = [
'key' => sanitize_text_field($query['key']),
'value' => sanitize_text_field($query['value']),
'compare' => isset($query['compare']) ? sanitize_text_field($query['compare']) : '='
];
}
}
}
}
$query = new WP_Query($args);
$posts_data = [];
foreach ($query->posts as $post) {
$posts_data[] = [
'id' => $post->ID,
'title' => get_the_title($post),
'link' => get_permalink($post)
];
}
return [
'posts' => $posts_data,
'total' => (int) $query->found_posts,
'pages' => (int) $query->max_num_pages
];
}
Пояснения к реализации
Мы создаём новый маршрут /wp-json/wpapi/v1/posts-filter/, который принимает параметр meta_query в формате JSON, а также пагинацию. В ответ возвращаем упрощённый список записей с ID, заголовком и ссылкой, а также данные о количестве страниц.
Это позволит клиентам гибко строить запросы с несколькими условиями по метаполям, например:
/wp-json/wpapi/v1/posts-filter/?meta_query=[{"key":"color","value":"blue"},{"key":"size","value":"large"}]&per_page=5&page=2
Использование плагинов для расширения фильтрации REST API
Если хотите готовое решение с минимальным кодом, обратите внимание на плагины, которые расширяют WP REST API:
- Clearfy Pro — содержит расширенные настройки безопасности и оптимизации REST API, включая фильтрацию и ограничение доступа.
- WPRemark — плагин для расширения функционала REST API с поддержкой кастомных полей и фильтров.
Использование плагинов позволяет быстрее внедрить фильтрацию, но собственный код даёт максимальную гибкость и контроль.
Советы по безопасности и производительности при фильтрации по метаполям
При фильтрации по метаполям важно учитывать несколько моментов:
- Всегда валидируйте и санитизируйте входящие данные, чтобы избежать SQL-инъекций и XSS.
- Метаполя не индексируются по умолчанию, поэтому сложные запросы по ним могут быть медленными. Для ускорения стоит создать индексы в базе данных или использовать кэширование ответов.
- Ограничивайте количество возвращаемых записей через параметр
per_pageи реализуйте пагинацию. - При большом объёме данных рассмотрите использование внешних решений для поиска и фильтрации, например, Elasticsearch с интеграцией через REST API.