Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

REST API

WordPress умеет отдавать содержимое не только страницами, но и данными в формате JSON. Через REST API работает сам редактор записей, а ещё мобильные приложения и сайты, где WordPress служит только хранилищем содержимого.

Встроенные адреса

Откройте в браузере адрес сайта с /wp-json/wp/v2/posts — придёт список записей:

АдресЧто возвращает
/wp-json/wp/v2/postsЗаписи
/wp-json/wp/v2/posts/15Запись с номером 15
/wp-json/wp/v2/pagesСтраницы
/wp-json/wp/v2/categoriesРубрики
/wp-json/wp/v2/mediaМедиафайлы
/wp-json/wp/v2/booksСвой тип записей с show_in_rest

Параметры уточняют запрос: ?per_page=5&search=php&_fields=id,title,link.

Свой маршрут

// books.php
 
add_action('rest_api_init', function () {
    register_rest_route('books/v1', '/top', [
        'methods' => 'GET',
        'callback' => 'books_rest_top',
        'permission_callback' => '__return_true',
    ]);
});
 
function books_rest_top(WP_REST_Request $request): WP_REST_Response
{
    $books = get_posts([
        'post_type' => 'book',
        'numberposts' => 5,
        'meta_key' => 'book_rating',
        'orderby' => 'meta_value_num',
    ]);
 
    $data = array_map(fn (WP_Post $book) => [
        'id' => $book->ID,
        'title' => $book->post_title,
        'link' => get_permalink($book),
    ], $books);
 
    return rest_ensure_response($data);
}

Маршрут откроется по адресу /wp-json/books/v1/top. Первая часть — books/v1 — пространство имён с номером версии.

Параметры

Параметры описывают в маршруте — WordPress сам проверит и очистит их:

register_rest_route('books/v1', '/books/(?P<id>\d+)/rating', [
    'methods' => 'POST',
    'callback' => 'books_rest_rate',
    'permission_callback' => fn () => is_user_logged_in(),
    'args' => [
        'id' => [
            'validate_callback' => fn ($value) => get_post_type((int) $value) === 'book',
        ],
        'rating' => [
            'required' => true,
            'type' => 'integer',
            'minimum' => 1,
            'maximum' => 5,
        ],
    ],
]);
 
function books_rest_rate(WP_REST_Request $request): WP_REST_Response
{
    $id = (int) $request['id'];
 
    update_post_meta($id, 'book_rating', (int) $request['rating']);
 
    return rest_ensure_response(['id' => $id, 'rating' => (int) $request['rating']]);
}

При неверных данных клиент получит ответ с кодом 400 и описанием ошибки.

Ошибки

function books_rest_show(WP_REST_Request $request): WP_REST_Response|WP_Error
{
    $book = get_post((int) $request['id']);
 
    if (!$book || $book->post_type !== 'book') {
        return new WP_Error('book_not_found', 'Книга не найдена', ['status' => 404]);
    }
 
    return rest_ensure_response(['title' => $book->post_title]);
}

Свои поля во встроенных ответах

add_action('rest_api_init', function () {
    register_rest_field('book', 'price', [
        'get_callback' => fn (array $post) => (int) get_post_meta($post['id'], 'book_price', true),
    ]);
});

Теперь в ответе /wp-json/wp/v2/books у каждой книги есть поле price.

Запрос из JavaScript

Для запросов от имени вошедшего пользователя нужен токен в заголовке X-WP-Nonce:

wp_add_inline_script('mytheme-app', 'const booksApi = ' . wp_json_encode([
    'root' => esc_url_raw(rest_url('books/v1/')),
    'nonce' => wp_create_nonce('wp_rest'),
]) . ';', 'before');
// assets/app.js
const response = await fetch(booksApi.root + 'books/15/rating', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': booksApi.nonce,
    },
    body: JSON.stringify({ rating: 5 }),
});
 
const result = await response.json();

Частая ошибка

permission_callback обязателен. Значение '__return_true' означает «доступно всем» и подходит только для чтения открытых данных. Маршрут, который что-то изменяет, должен проверять права — current_user_can('edit_posts') — иначе менять данные сайта сможет любой посетитель, знающий адрес.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.