Створення Маршрутів
Про те, що таке Маршрути та Ендпоінти ми вже говорили , а в цьому розділі розглянемо, як створювати свої маршрути, у яких будуть свої кінцеві точки.
Створення маршрутів ґрунтується лише на одній функції register_rest_route() . По суті, все, що потрібно, щоб створити свій маршрут і кінцеві точки – це розібратися, як користуватися цією функцією (дивіться її опис).
Нижче розглянемо простий приклад створення маршруту, а потім розширимо його до створення маршруту на основі класу контролера.
Простий приклад створення маршруту
Почнемо зі створення простої PHP функції
## Отримує пости вказаного автора function my_awesome_func( WP_REST_Request $request ){ $posts = get_posts(array( 'author' => (int) $request['id'], ))); if (empty($posts)) return new WP_Error( 'no_author_posts', 'Записів не знайдено', [ 'status' => 404 ] ); return $posts; }
Щоб результат цієї функції став доступним з API, нам потрібно зареєструвати маршрут і прикріпити до нього цю функцію. Тоді на запит до цього маршруту, користувач побачить відповідь цієї функції.
Маршрут реєструється функцією rest_api_init , щоб не виконувати жодних операцій, якщо REST API вимкнено.
У функцію register_rest_route() потрібно передати три параметри: простір імен , маршрут та опції маршруту (в опціях вказуємо назву нашої функції php).
add_action( 'rest_api_init', function(){ register_rest_route( 'myplugin/v1', '/author-posts/(?P<id>d+)', [ 'methods' => 'GET', 'callback' => 'my_awesome_func', ]); } );
Чому простір імен називається саме myplugin/v1
, читайте тут .
Маршрут /author-posts/(?P<id>d+)
вказаний як регулярний вираз, тому що ми хочемо, щоб на URL /author-posts/{id}
нам видавалися пости автора вказаного замість {id} .
Тепер, пройшовши за посиланням http://example.com/wp-json/myplugin/v1/author-posts/1
на нашому сайті, ми побачимо JSON відповідь, яку повернула наша функція my_awesome_func() .
GET запит на URL http://example.com/wp-json/myplugin/v1/author-posts/1
– це кінцева точка, яку ми описали при реєстрації маршруту.
Маршрут може мати багато кінцевих точок, і для кожної кінцевої точки можна вказати різні параметри:
- methods – HTTP методи за якими потрібно звертатися до кінцевої точки
- callback — функцію зворотного дзвінка для відповіді на запит
- permission_callback — функцію зворотного дзвінка для перевірки права доступу до кінцевої точки.
- args — параметри запиту, які можна надіслати кінцевій точці. Кожен параметр описується як елемент масиву. Для кожного параметра можна вказати свої опції, наприклад, чи це обов’язковий параметр (required) або значення параметра за замовчуванням (default), функцію перевірки значення параметра (validate_callback).
Докладніше про всі параметри та аргументи читайте в описі функції register_rest_route() .
Щоб було зрозуміліше, давайте розширимо наш маршрут і додамо йому ще одну кінцеву точку, до якої може звернутися лише авторизований користувач і тільки запит POST, і якій можна передати два параметри: arg_str і arg_int .
add_action( 'rest_api_init', function(){ register_rest_route( 'myplugin/v1', '/author-posts/(?P<id>d+)', array( array( 'methods' => 'GET', 'callback' => 'my_awesome_func', ), array( 'methods' => 'POST', 'callback' => 'my_awesome_func_two', 'args' => array( 'arg_str' => array( 'type' => 'string', // значення параметра має бути рядком 'required' => true, // параметр обов'язковий ), 'arg_int' => array( 'type' => 'integer', // значення параметра має бути числом 'default' => 10, // значення за промовчанням = 10 ), ), 'permission_callback' => function( $request ){ // Тільки авторизований користувач має доступ до ендпоінту return is_user_logged_in(); }, // або у разі можна записати простіше 'permission_callback' => 'is_user_logged_in', ) ))); } );
У цьому прикладі для другої кінцевої точки (POST) використовується функція my_awesome_func_two вона повинна повернути відповідь, якщо користувач авторизований та вказано обов’язковий параметр arg_str . Також вона отримає два параметри запиту. Функції my_awesome_func_two() у нас ще немає, тому давайте її створимо, нехай вона у відповідь на запит поверне передані параметри запиту:
function my_awesome_func_two( WP_REST_Request $request ){ $response = array( 'arg_str' => $request->get_param('arg_str'), 'arg_int' => $request->get_param('arg_int') ); return $response; }
Перевірка створеного маршруту
Тепер перевіримо, як усе працює, спробуємо надіслати запити на наші ендпоінти.
Надсилаємо GET запит:
GET http://example.com/wp-json/myplugin/v1/author-posts/1
Отримаємо відповідь:
[ { "ID": 72, "post_author": "1", "post_content": "Контент запису", "post_title": "Заголовок запису", ... "comment_count": "0", }, { ... }, {...} ]
Надсилаємо POST запит:
POST http://example.com/wp-json/myplugin/v1/author-posts/1
Отримуємо помилку, тому що ми не авторизовані:
{ "code": "empty_username", "message": "<strong>ERROR</strong>: Username field is empty.", "data": null, "additional_errors": [ { "code": "empty_password", "message": "<strong>ERROR</strong>: password field is empty.", "data": null } ] }
Авторизуємося , надсилаємо запит ще раз, і отримуємо помилку, тому що ми не вказали обов’язковий параметр ‘arg_str’.
{ "code": "rest_missing_callback_param", "message": "Відсутний параметр: arg_str", "data": { "status": 400, "params": [ "arg_str" ] } }
Вкажемо параметри
POST http://example.com/wp-json/myplugin/v1/author-posts/1?arg_str=foo&arg_int=bar
Знову отримуємо помилку, тому що тип параметра arg_int у нас вказано integer (число), а ми вказали рядок:
{ "code": "rest_invalid_param", "message": "Неправильний параметр: arg_int", "data": { "status": 400, "params": { "arg_int": "arg_int не належить до типу integer." } } }
Виправимо значення:
POST http://example.com/wp-json/myplugin/v1/author-posts/1?arg_str=foo&arg_int=123
Отримуємо очікувану відповідь:
{ "arg_str": "foo", "arg_int": 123 }
Пояснення до аргументів під час створення маршруту
Дивіться опис функції register_rest_route() .
Створення маршруту на основі Класу контролера (ООП)
Докладніше про Класи контролерів читайте в однойменному розділі .
При реєстрації нового маршруту та його кінцевих точок рекомендується використовувати клас контролера, тому що він робить код зручнішим, стабільнішим і легко розширюваним.
Тут все по суті також в основі лежить функція register_rest_route() .
Суть створення класу контролера у зручності коду та його розширюваності – у тому щоб створити один клас, який працюватиме з одним ресурсом. Але може мати кілька маршрутів, різні параметри маршрутів, єдину схему ресурсу (яку до речі можна описати при створенні маршруту) і багато іншого.
Тобто. створюючи клас контролера, ми також, як і в прикладі вище, реєструємо нові маршрути, але при цьому створюємо логічний код, що розширюється на основі ООП.
Яким має бути клас контролер продумано розробниками WP та описано в абстрактному класі WP_REST_Controller
До завдань контролера входить:
- Отримання вхідних даних.
- генерація вихідних даних.
У WP REST API наші контролери будуть отримувати запит як об’єкт rest_ensure_response() ).
Приклад створення Класу контролера
// Запускаємо наш контролер та реєструємо маршрути add_action( 'rest_api_init', 'prefix_register_my_rest_routes' ); function prefix_register_my_rest_routes() { $controller = New My_REST_Posts_Controller(); $controller->register_routes(); } class My_REST_Posts_Controller extends WP_REST_Controller { function __construct(){ $this->namespace = 'my-namespace/v1'; $this->rest_base = 'posts'; } function register_routes(){ register_rest_route( $this->namespace, "/$this->rest_base", [ [ 'methods' => 'GET', 'callback' => [ $this, 'get_items' ], 'permission_callback' => [ $this, 'get_items_permissions_check' ], ], 'schema' => [ $this, 'get_item_schema' ], ]); register_rest_route( $this->namespace, "/$this->rest_base/(?P<id>[w]+)", [ [ 'methods' => 'GET', 'callback' => [ $this, 'get_item' ], 'permission_callback' => [ $this, 'get_item_permissions_check' ], ], 'schema' => [ $this, 'get_item_schema' ], ]); } function get_items_permissions_check( $request ){ if ( ! current_user_can( 'read' ) ) return new WP_Error( 'rest_forbidden', esc_html__( 'You cannot view the post resource.' ), [ 'status' => $this->error_status_code() ] ); return true; } /** * Отримує останні пости та віддає їх у вигляді rest відповіді. * * @param WP_REST_Request $request Поточний запит. * * @return WP_Error|array */ function get_items ($ request) { $ data = []; $posts = get_posts([ 'post_per_page' => 5, ]); if (empty($posts)) return $data; foreach( $posts as $post ){ $response = $this->prepare_item_for_response( $post, $request ); $data[] = $this->prepare_response_for_collection( $response ); } return $data; } ## Перевірка права доступу. function get_item_permissions_check( $request ){ return $this->get_items_permissions_check( $request ); } /** * Отримує окремий ресурс. * * @param WP_REST_Request $request Поточний запит. * * @return array */ function get_item( $request ){ $id = (int) $request['id']; $post = get_post($id); if(! $post) return array(); return $this->prepare_item_for_response( $post, $request ); } /** * Збирає дані ресурсу відповідно до схеми ресурсу. * * @param WP_Post $post Об'єкт ресурсу, з якого буде взято оригінальні дані. * @param WP_REST_Request $request Поточний запит. * * @return array */ function prepare_item_for_response( $post, $request ){ $post_data = []; $schema = $this->get_item_schema(); / / We are also renaming the fields to more understandable names. if ( isset( $schema['properties']['id'] ) ) $post_data['id'] = (int) $post->ID; if ( isset( $schema['properties']['content'] ) ) $post_data['content'] = apply_filters( 'the_content', $post->post_content, $post ); return $post_data; } /** * Підготовка відповіді окремого ресурсу для додавання його до колекції ресурсів. * * @param WP_REST_Response $response Response object. * * @return array|mixed Response data, ready for insertion into collection data. */ function prepare_response_for_collection( $response ){ if ( ! ( $response instanceof WP_REST_Response ) ){ return $response; } $ data = (array) $ response-> get_data (); $server = rest_get_server(); if ( method_exists( $server, 'get_compact_response_links' ) ){ $links = call_user_func([ $server, 'get_compact_response_links' ], $response ); } else { $links = call_user_func([ $server, 'get_response_links' ], $response ); } if ( ! empty( $links ) ){ $data['_links'] = $links; } return $data; } ## Схема ресурсу. function get_item_schema(){ $schema = [ // показує яку версію схеми ми використовуємо – це draft 4 '$schema' => 'http://json-schema.org/draft-04/schema#', // визначає ресурс, який описує схема 'title' => 'vehicle', 'type' => 'object', // У JSON схемі потрібно вказувати властивості в атрибуеті 'properties'. 'properties' => [ 'id' => [ 'description' => 'Unique identifier for the object.', 'type' => 'integer', 'context' => [ 'view', 'edit', 'embed' ], 'readonly' => true, ], 'vin' => [ 'description' => 'VIN code of vehicle.', 'type' => 'string', ], // TODO додати поля // [] ], ]; return $schema; } ## Встановлює HTTP статус код для авторизації. function error_status_code(){ return is_user_logged_in() ? 403: 401; } }
Ще приклади Класів контролера дивіться в ядрі WordPress. Ось деякі з них: