WP REST API пагинация с кастомными метаданными

Пагинация — одна из ключевых задач при работе с REST API в WordPress, особенно если необходимо выводить данные с учетом пользовательских метаданных. В стандартных эндпоинтах WordPress пагинация работает по параметрам page и per_page, однако при фильтрации или сортировке по кастомным полям это часто становится проблемой. В этой статье разберем, как реализовать эффективную пагинацию в WP REST API с учетом пользовательских метаданных, используя примеры кода и лучшие практики.

Почему стандартная пагинация WP REST API не всегда подходит

Стандартные REST API эндпоинты, например /wp/v2/posts, поддерживают пагинацию по параметрам page и per_page. Однако если необходимо фильтровать записи по метаданным, например, вывести товары с определенным значением поля price или посты с кастомным статусом, то стандартный механизм пагинации может не работать корректно, так как WP_Query с мета-запросами требует особой обработки.

Без правильной обработки пагинация может показывать некорректное количество страниц, повторяющиеся записи или вовсе пропускать данные. Поэтому нужно создавать кастомные REST API эндпоинты с поддержкой пагинации и фильтрации.

Создаем кастомный REST API эндпоинт с пагинацией и мета-запросами

Для начала зарегистрируем новый эндпоинт, который будет возвращать посты с фильтрацией по метаданным и поддержкой пагинации.

add_action('rest_api_init', function () {
    register_rest_route('wpapi/v1', '/custom-posts', [
        'methods' => 'GET',
        'callback' => 'wpapi_get_custom_posts',
        'permission_callback' => '__return_true',
        'args' => [
            'page' => [
                'validate_callback' => 'is_numeric',
                'default' => 1,
            ],
            'per_page' => [
                'validate_callback' => 'is_numeric',
                'default' => 10,
            ],
            'meta_key' => [
                'validate_callback' => 'is_string',
            ],
            'meta_value' => [
                'validate_callback' => 'is_string',
            ],
        ],
    ]);
});

function wpapi_get_custom_posts($request) {
    $page = (int)$request->get_param('page');
    $per_page = (int)$request->get_param('per_page');
    $meta_key = $request->get_param('meta_key');
    $meta_value = $request->get_param('meta_value');

    $args = [
        'post_type' => 'post',
        'posts_per_page' => $per_page,
        'paged' => $page,
    ];

    if ($meta_key && $meta_value) {
        $args['meta_query'] = [
            [
                'key' => $meta_key,
                'value' => $meta_value,
                'compare' => '=',
            ],
        ];
    }

    $query = new WP_Query($args);

    $posts = [];
    foreach ($query->posts as $post) {
        $posts[] = [
            'id' => $post->ID,
            'title' => get_the_title($post),
            'date' => $post->post_date,
            'meta' => get_post_meta($post->ID),
        ];
    }

    $total_posts = (int)$query->found_posts;
    $total_pages = (int)$query->max_num_pages;

    $response = new WP_REST_Response($posts, 200);
    $response->header('X-WP-Total', $total_posts);
    $response->header('X-WP-TotalPages', $total_pages);

    return $response;
}

В этом коде мы регистрируем эндпоинт /wpapi/v1/custom-posts с параметрами для пагинации и фильтрации по метаданным. В ответе передаем заголовки X-WP-Total и X-WP-TotalPages, чтобы клиент понимал общее количество записей и страниц.

Обработка пагинации на стороне клиента

При использовании этого API на фронтенде можно получать данные постранично, передавая параметры page и per_page. Пример запроса:

https://example.com/wp-json/wpapi/v1/custom-posts?page=2&per_page=5&meta_key=color&meta_value=red

Так вы получите вторую страницу постов, у которых метаполе color равно red. В ответе будет не более 5 записей и заголовки с общей статистикой.

Оптимизация запросов и кеширование результатов

Фильтрация по метаданным может быть ресурсоёмкой, особенно на больших базах данных. Чтобы улучшить производительность, рекомендуем:

  • Использовать индексы для мета-таблиц через запрос к базе данных.
  • Кешировать результаты с помощью Transients API или внешних кеш-систем.
  • Ограничивать максимальное значение per_page, чтобы не отдавать слишком большие объемы данных.

Пример кеширования результата запроса в функции обработчика:

function wpapi_get_custom_posts($request) {
    $page = (int)$request->get_param('page');
    $per_page = (int)$request->get_param('per_page');
    $meta_key = $request->get_param('meta_key');
    $meta_value = $request->get_param('meta_value');

    $cache_key = 'wpapi_custom_posts_' . md5(serialize([$page, $per_page, $meta_key, $meta_value]));
    $cached = get_transient($cache_key);
    if ($cached !== false) {
        return new WP_REST_Response($cached['posts'], 200, [
            'X-WP-Total' => $cached['total_posts'],
            'X-WP-TotalPages' => $cached['total_pages'],
        ]);
    }

    // ... здесь WP_Query как выше ...

    set_transient($cache_key, [
        'posts' => $posts,
        'total_posts' => $total_posts,
        'total_pages' => $total_pages,
    ], 5 * MINUTE_IN_SECONDS);

    return $response;
}

Используем плагины для расширения возможностей REST API

Для удобства можно использовать готовые решения из репозитория:

  • Clearfy Pro — для оптимизации запросов и кеширования.
  • WPRemark — удобный инструмент для расширения REST API, добавления новых полей и фильтров.

Они помогут уменьшить нагрузку и расширить функционал без необходимости писать весь код вручную.

Ошибки и отладка при работе с пагинацией и метаданными

Частые ошибки при реализации пагинации с мета-запросами:

  • Неправильное использование параметров paged и posts_per_page в WP_Query.
  • Отсутствие передачи заголовков X-WP-Total и X-WP-TotalPages в ответе, из-за чего клиент не понимает, сколько страниц всего.
  • Перекрытие фильтров и хуков, которые влияют на WP_Query в других частях сайта.

Для отладки используйте WP_DEBUG и функцию error_log(). Также можно временно выводить SQL-запросы с помощью плагина Query Monitor.

Шаблоны для WP Плагины для WP