/
shish
/
rb-test-task
Обзор
Документация
Войти
/
shish
/
rb-test-task
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
wp-theme/functions.php
906 строк
34 KB
ShishkaDanil
docs: вроде комментарии добавил, не помню
03 сен 2025, 17:48
03 сен 2025, 17:48
1d4adaf
Код
Авторство
О чём код?
<?php if (!defined('ABSPATH')) { exit; } // логирование http запросов function rb_get_client_ip() { $headers = [ 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_REAL_IP', 'HTTP_CF_CONNECTING_IP', 'REMOTE_ADDR', ]; foreach ($headers as $key) { if (!empty($_SERVER[$key])) { $value = (string) $_SERVER[$key]; if ($key === 'HTTP_X_FORWARDED_FOR') { $parts = array_map('trim', explode(',', $value)); foreach ($parts as $part) { if (filter_var($part, FILTER_VALIDATE_IP)) { return $part; } } continue; } if (filter_var($value, FILTER_VALIDATE_IP)) { return $value; } } } return null; } function rb_ensure_logs_table() { global $wpdb; $table_name = $wpdb->prefix . 'rb_api_logs'; $installed_ver = get_option('rb_api_logs_db_version'); $target_ver = '1.0'; if ($installed_ver === $target_ver && $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table_name)) === $table_name) { return; } // это для таблиц бд require_once ABSPATH . 'wp-admin/includes/upgrade.php'; // я посчитал, что SQL запросом будет лучше $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, method varchar(10) NOT NULL, route varchar(191) NOT NULL, status smallint(3) unsigned NULL, ip varchar(45) NULL, user_agent varchar(255) NULL, created_at datetime NOT NULL, PRIMARY KEY (id), KEY route_idx (route), KEY created_idx (created_at) ) $charset_collate;"; dbDelta($sql); update_option('rb_api_logs_db_version', $target_ver); } add_action('after_switch_theme', 'rb_ensure_logs_table'); add_action('init', function () { rb_ensure_logs_table(); }); function rb_log_rest_post_dispatch($response, $server, $request) { try { if (!($request instanceof WP_REST_Request)) { return $response; } $route = $request->get_route(); if (!is_string($route) || strpos($route, '/books/') !== 0) { return $response; } $status = null; if ($response instanceof WP_HTTP_Response) { $status = (int) $response->get_status(); } elseif (is_wp_error($response)) { $data = $response->get_error_data(); if (is_array($data) && isset($data['status'])) { $status = (int) $data['status']; } } global $wpdb; $table = $wpdb->prefix . 'rb_api_logs'; // обрезание юзер агента $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? substr((string) $_SERVER['HTTP_USER_AGENT'], 0, 255) : null; $created_at = current_time('mysql'); $wpdb->insert( $table, [ 'method' => (string) $request->get_method(), 'route' => (string) $route, 'status' => $status, 'ip' => rb_get_client_ip(), 'user_agent' => $user_agent, 'created_at' => $created_at, ], ['%s', '%s', '%d', '%s', '%s', '%s'] // это формат для бд ); } catch (\Throwable $e) { rb_handle_exception($e, 'rb_log_rest_post_dispatch'); } return $response; } add_filter('rest_post_dispatch', 'rb_log_rest_post_dispatch', 10, 3); function rb_rest_error($code, $message, $status = 400) { return new WP_Error($code, $message, ['status' => $status]); } // функция, чтобы ловить ошибки function rb_handle_exception($e, $context = 'rb') { if ($e instanceof \Throwable || $e instanceof \Exception) { error_log(sprintf('[%s] %s in %s:%d', $context, $e->getMessage(), $e->getFile(), $e->getLine())); } else { error_log(sprintf('[%s] Unknown error type encountered', $context)); } return rb_rest_error('rb_internal_error', 'Internal server error', 500); } // источники ключей: constant RB_API_KEY, option rb_api_key, env RB_API_KEY. function rb_check_authentication($request) { try { // Уже авторизованный администратор if (function_exists('is_user_logged_in') && is_user_logged_in() && current_user_can('manage_options')) { return true; } // API ключ в заголовке $provided_key = null; if ($request instanceof WP_REST_Request) { $provided_key = $request->get_header('x-api-key'); if (!$provided_key) { $provided_key = $request->get_header('x-wp-api-key'); } if (!$provided_key) { $provided_key = $request->get_header('rb-api-key'); } } else { $candidates = ['HTTP_X_API_KEY', 'HTTP_X_WP_API_KEY', 'HTTP_RB_API_KEY']; foreach ($candidates as $server_key) { if (!empty($_SERVER[$server_key])) { $provided_key = $_SERVER[$server_key]; break; } } } if (is_string($provided_key) && $provided_key !== '') { $valid_keys = []; $opt_key = get_option('rb_api_key'); if (is_string($opt_key) && $opt_key !== '') { $valid_keys[] = $opt_key; } if (defined('RB_API_KEY') && is_string(RB_API_KEY) && RB_API_KEY !== '') { $valid_keys[] = RB_API_KEY; } $env_key = getenv('RB_API_KEY'); if (is_string($env_key) && $env_key !== '') { $valid_keys[] = $env_key; } if (!empty($valid_keys) && in_array($provided_key, $valid_keys, true)) { return true; } } // Basic Auth $auth_header = null; if ($request instanceof WP_REST_Request) { $auth_header = $request->get_header('authorization'); } if (!$auth_header && isset($_SERVER['HTTP_AUTHORIZATION'])) { $auth_header = $_SERVER['HTTP_AUTHORIZATION']; } if (!$auth_header && isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) { $auth_header = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; } // вариант через PHP_AUTH_USER/PHP_AUTH_PW if (!$auth_header && isset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])) { $username = (string) $_SERVER['PHP_AUTH_USER']; $password = (string) $_SERVER['PHP_AUTH_PW']; $user = wp_authenticate($username, $password); if ($user instanceof WP_User) { if (user_can($user, 'manage_options')) { return true; } return rb_rest_error('rb_forbidden', 'Требуются права администратора', 403); } return rb_rest_error('rb_unauthorized', 'Неверные учетные данные', 401); } if (is_string($auth_header) && stripos($auth_header, 'basic ') === 0) { $encoded = trim(substr($auth_header, 6)); $decoded = base64_decode($encoded); if ($decoded !== false && strpos($decoded, ':') !== false) { list($username, $password) = explode(':', $decoded, 2); $user = wp_authenticate((string) $username, (string) $password); if ($user instanceof WP_User) { if (user_can($user, 'manage_options')) { return true; } return rb_rest_error('rb_forbidden', 'Требуются права администратора', 403); } } return rb_rest_error('rb_unauthorized', 'Неверные учетные данные', 401); } return rb_rest_error('rb_unauthorized', 'Отсутствует авторизация', 401); } catch (\Throwable $e) { return rb_handle_exception($e, 'rb_check_authentication'); } } function rb_register_book_post_type() { // регистрация типа register_post_type('book', [ 'labels' => [ 'name' => 'Books', 'singular_name' => 'Book', 'add_new' => 'Add New', 'add_new_item' => 'Add New Book', 'edit_item' => 'Edit Book', 'new_item' => 'New Book', 'view_item' => 'View Book', 'search_items' => 'Search Books', 'not_found' => 'No Books Found', ], 'public' => true, 'has_archive' => true, 'show_in_rest' => true, 'supports' => ['title', 'editor', 'custom-fields'], ]); } // функция, чтоб превратить json в массив function rb_sanitize_genres_meta($value, $meta_key, $object_type) { if (is_string($value)) { $decoded = json_decode($value, true); if (json_last_error() === JSON_ERROR_NONE) { $value = $decoded; } else { return []; } } // если в данных мусор if (!is_array($value)) { return []; } $sanitized = []; foreach ($value as $item) { if (is_string($item)) { $sanitized[] = sanitize_text_field($item); } } return $sanitized; } // логика подсчёта рейтинга function rb_calculate_rating($publish_year, $genres_count) { $current_year = (int) date('Y'); $year = (int) $publish_year; $genres_count_int = (int) $genres_count; if ($year > $current_year) { $year = $current_year; } if ($genres_count_int < 0) { $genres_count_int = 0; } $age_component = ($current_year - $year) / 20; $rating = (float) ($age_component + $genres_count_int); if ($rating < 2.0) { $rating = 2.0; } return $rating; } function rb_validate_book_data($data, $is_update = false) { $errors = []; if (!is_array($data)) { return ['Неверный формат данных']; } // обязательные поляи if (!$is_update) { if (!isset($data['title']) || !is_string($data['title']) || trim($data['title']) === '') { $errors[] = 'Поле title обязательно и должно быть непустой строкой'; } if (!isset($data['author']) || !is_string($data['author']) || trim($data['author']) === '') { $errors[] = 'Поле author обязательно и должно быть непустой строкой'; } } else { if (isset($data['title']) && (!is_string($data['title']) || trim($data['title']) === '')) { $errors[] = 'Поле title, если указано, должно быть непустой строкой'; } if (isset($data['author']) && (!is_string($data['author']) || trim($data['author']) === '')) { $errors[] = 'Поле author, если указано, должно быть непустой строкой'; } } // publish_year: диапазон 1931..текущий год if (isset($data['publish_year'])) { $year = is_numeric($data['publish_year']) ? (int) $data['publish_year'] : null; if ($year === null) { $errors[] = 'publish_year должен быть целым числом'; } else { $current_year = (int) date('Y'); if ($year < 1931 || $year > $current_year) { $errors[] = 'publish_year должен быть в диапазоне 1931–' . $current_year; } // новый год не старше исходного if ($is_update) { $original_year = null; // в первую очередь надо получить явно переданное поле if (isset($data['original_publish_year']) && is_numeric($data['original_publish_year'])) { $original_year = (int) $data['original_publish_year']; } elseif (isset($data['existing_publish_year']) && is_numeric($data['existing_publish_year'])) { $original_year = (int) $data['existing_publish_year']; } elseif (isset($data['current_publish_year']) && is_numeric($data['current_publish_year'])) { $original_year = (int) $data['current_publish_year']; } else { // попытка получить из бд, если передан айди $possible_ids = ['ID', 'id', 'post_id']; foreach ($possible_ids as $key) { if (isset($data[$key]) && is_numeric($data[$key])) { $meta = get_post_meta((int) $data[$key], 'publish_year', true); if (is_numeric($meta)) { $original_year = (int) $meta; } break; } } } if ($original_year !== null && $year < $original_year) { $errors[] = 'Новый publish_year не может быть меньше исходного (' . $original_year . ')'; } } } } // genres максимум 3 элемента и только строки if (isset($data['genres'])) { $genres = $data['genres']; if (is_string($genres)) { $decoded = json_decode($genres, true); if (json_last_error() === JSON_ERROR_NONE) { $genres = $decoded; } } if (!is_array($genres)) { $errors[] = 'genres должен быть массивом строк (максимум 3 элемента)'; } else { if (count($genres) > 3) { $errors[] = 'genres может содержать не более 3 элементов'; } foreach ($genres as $g) { if (!is_string($g)) { $errors[] = 'genres должен содержать только строки'; break; } } } } return empty($errors) ? true : $errors; } function rb_register_book_meta() { register_post_meta('book', 'author', [ 'type' => 'string', 'single' => true, 'show_in_rest' => true, ]); register_post_meta('book', 'publish_year', [ 'type' => 'integer', 'single' => true, 'show_in_rest' => true, ]); register_post_meta('book', 'genres', [ 'type' => 'array', 'single' => true, 'show_in_rest' => [ 'schema' => [ 'type' => 'array', 'items' => [ 'type' => 'string', ], ], ], 'sanitize_callback' => 'rb_sanitize_genres_meta', ]); register_post_meta('book', 'rating', [ 'type' => 'number', 'single' => true, 'show_in_rest' => true, ]); } // [GET] /books/v1/ function rb_get_books($request) { try { if (!post_type_exists('book')) { return rb_rest_error('rb_post_type_missing', 'Post type "book" is not registered', 500); } // пагинация $page_raw = ($request instanceof WP_REST_Request) ? $request->get_param('page') : (isset($request['page']) ? $request['page'] : null); $limit_raw = ($request instanceof WP_REST_Request) ? $request->get_param('limit') : (isset($request['limit']) ? $request['limit'] : null); $page = (is_numeric($page_raw) && (int) $page_raw > 0) ? (int) $page_raw : 1; $limit = (is_numeric($limit_raw) && (int) $limit_raw > 0) ? (int) $limit_raw : 10; if ($limit > 100) { $limit = 100; // от слишком больших выборок } // фильтры $author_filter_raw = ($request instanceof WP_REST_Request) ? $request->get_param('author') : (isset($request['author']) ? $request['author'] : null); $genres_filter_raw = ($request instanceof WP_REST_Request) ? $request->get_param('genres') : (isset($request['genres']) ? $request['genres'] : null); $meta_query = []; if (is_string($author_filter_raw) && trim($author_filter_raw) !== '') { $author_filter = sanitize_text_field($author_filter_raw); $meta_query[] = [ 'key' => 'author', 'value' => $author_filter, 'compare' => 'LIKE', ]; } // жанры могут быть строкой с запятыми или массивом и соответствовать любому из предоставленных $genres_terms = []; if (is_string($genres_filter_raw) && trim($genres_filter_raw) !== '') { $parts = array_map('trim', explode(',', $genres_filter_raw)); foreach ($parts as $p) { if ($p !== '') { $genres_terms[] = sanitize_text_field($p); } } } elseif (is_array($genres_filter_raw)) { foreach ($genres_filter_raw as $g) { if (is_string($g) && trim($g) !== '') { $genres_terms[] = sanitize_text_field($g); } } } if (!empty($genres_terms)) { $genres_group = ['relation' => 'OR']; foreach ($genres_terms as $term) { $genres_group[] = [ 'key' => 'genres', 'value' => $term, 'compare' => 'LIKE', ]; } $meta_query[] = $genres_group; } if (!empty($meta_query)) { $meta_query = array_merge(['relation' => 'AND'], $meta_query); } $args = [ 'post_type' => 'book', 'posts_per_page' => $limit, 'paged' => $page, 'orderby' => 'meta_value_num', 'meta_key' => 'rating', 'order' => 'DESC', ]; if (!empty($meta_query)) { $args['meta_query'] = $meta_query; } $query = new WP_Query($args); if (!($query instanceof WP_Query)) { return rb_rest_error('rb_query_failed', 'Failed to query books', 500); } $books = []; if (is_array($query->posts)) { foreach ($query->posts as $post) { $author = get_post_meta($post->ID, 'author', true); $rating_meta = get_post_meta($post->ID, 'rating', true); $rating = is_numeric($rating_meta) ? (float)$rating_meta : 0.0; $publish_year = get_post_meta($post->ID, 'publish_year', true); $publish_year = is_numeric($publish_year) ? (int)$publish_year : null; // определение раритетности $rarity = null; if ($publish_year !== null && $publish_year < 1950) { $rarity = 'Раритет'; } $book_data = [ 'ID' => $post->ID, 'title' => $post->post_title, 'author' => is_string($author) ? $author : '', 'rating' => $rating, ]; $books[] = $book_data; } } $response = rest_ensure_response($books); if ($response instanceof WP_REST_Response) { $response->header('X-Total-Items', (string) intval($query->found_posts)); $response->header('X-Total-Pages', (string) intval($query->max_num_pages)); $response->header('X-Current-Page', (string) $page); $response->header('X-Limit', (string) $limit); } return $response; } catch (\Throwable $e) { return rb_handle_exception($e, 'rb_get_books'); } } // [GET] /books/v1/(?P<id>\d+) function rb_get_book_by_id($request) { try { $id_raw = ($request instanceof WP_REST_Request) ? $request->get_param('id') : (isset($request['id']) ? $request['id'] : null); $id = is_numeric($id_raw) ? (int) $id_raw : 0; if ($id <= 0) { return rb_rest_error('rb_invalid_id', 'Некорректный ID', 400); } $post = get_post($id); if (!($post instanceof WP_Post) || $post->post_type !== 'book') { return rb_rest_error('rb_not_found', 'Книга не найдена', 404); } $author = get_post_meta($id, 'author', true); $rating_meta = get_post_meta($id, 'rating', true); $rating = is_numeric($rating_meta) ? (float) $rating_meta : 0.0; $publish_year_meta = get_post_meta($id, 'publish_year', true); $publish_year = is_numeric($publish_year_meta) ? (int) $publish_year_meta : null; $genres = get_post_meta($id, 'genres', true); $genres = is_array($genres) ? array_values(array_map('strval', $genres)) : []; $response = [ 'ID' => $post->ID, 'title' => $post->post_title, 'content' => $post->post_content, 'author' => is_string($author) ? $author : '', 'rating' => $rating, 'genres' => $genres, ]; if ($publish_year !== null) { $response['publish_year'] = $publish_year; } // рекомендация по дням недели вторник/среда $weekday = (int) date_i18n('N'); if ($weekday === 2 || $weekday === 3) { $response['recommendation'] = 'Рекомендуем по вторникам и средам'; } return rest_ensure_response($response); } catch (\Throwable $e) { return rb_handle_exception($e, 'rb_get_book_by_id'); } } // [PUT] /books/v1/(?P<id>\d+) function rb_update_book($request) { try { $id_raw = ($request instanceof WP_REST_Request) ? $request->get_param('id') : (isset($request['id']) ? $request['id'] : null); $id = is_numeric($id_raw) ? (int) $id_raw : 0; if ($id <= 0) { return rb_rest_error('rb_invalid_id', 'Некорректный ID', 400); } $post = get_post($id); if (!($post instanceof WP_Post) || $post->post_type !== 'book') { return rb_rest_error('rb_not_found', 'Книга не найдена', 404); } $data = $request->get_json_params(); if (!is_array($data) || empty($data)) { $data = $request->get_params(); } if (!is_array($data) || empty($data)) { return rb_rest_error('rb_no_data', 'Нет данных для обновления', 400); } // это валидация $current_year_meta = get_post_meta($id, 'publish_year', true); if (is_numeric($current_year_meta)) { $data['original_publish_year'] = (int) $current_year_meta; } $validation = rb_validate_book_data($data, true); if ($validation !== true) { $message = is_array($validation) ? implode('; ', $validation) : 'Validation failed'; return rb_rest_error('rb_validation_error', $message, 400); } // обновление только переданных полей т.к. (вообще то, частичное обновление полей это PATCH, но тут PUT, но в ТЗ сказано что нужно частично обновлять поля) $post_update = []; if (isset($data['title'])) { $post_update['post_title'] = sanitize_text_field($data['title']); } if (isset($data['content'])) { $post_update['post_content'] = wp_kses_post($data['content']); } if (!empty($post_update)) { $post_update['ID'] = $id; $result = wp_update_post($post_update, true); if (is_wp_error($result)) { return rb_rest_error('rb_update_failed', $result->get_error_message(), 500); } } $genres_changed = false; $year_changed = false; if (array_key_exists('author', $data)) { update_post_meta($id, 'author', sanitize_text_field((string) $data['author'])); } if (array_key_exists('publish_year', $data)) { if (is_numeric($data['publish_year'])) { update_post_meta($id, 'publish_year', (int) $data['publish_year']); $year_changed = true; } else { delete_post_meta($id, 'publish_year'); $year_changed = true; } } if (array_key_exists('genres', $data)) { $genres = $data['genres']; if (is_string($genres)) { $decoded = json_decode($genres, true); if (json_last_error() === JSON_ERROR_NONE) { $genres = $decoded; } } $sanitized = []; if (is_array($genres)) { foreach ($genres as $g) { if (is_string($g)) { $sanitized[] = sanitize_text_field($g); } } if (count($sanitized) > 3) { $sanitized = array_slice($sanitized, 0, 3); } } update_post_meta($id, 'genres', $sanitized); $genres_changed = true; } // пересчёт рейтинга при изменении года или жанров if ($year_changed || $genres_changed) { $year_meta = get_post_meta($id, 'publish_year', true); $year_val = is_numeric($year_meta) ? (int) $year_meta : (int) date('Y'); $genres_meta = get_post_meta($id, 'genres', true); $genres_count = is_array($genres_meta) ? count($genres_meta) : 0; $new_rating = rb_calculate_rating($year_val, $genres_count); update_post_meta($id, 'rating', (float) $new_rating); } // вернуть обновлённые данные $req = new WP_REST_Request('GET', '/books/v1/' . $id); if (method_exists($req, 'set_url_params')) { $req->set_url_params(['id' => $id]); } return rb_get_book_by_id($req); } catch (\Throwable $e) { return rb_handle_exception($e, 'rb_update_book'); } } // [DELETE] /books/v1/(?P<id>\d+) function rb_delete_book($request) { try { $id_raw = ($request instanceof WP_REST_Request) ? $request->get_param('id') : (isset($request['id']) ? $request['id'] : null); $id = is_numeric($id_raw) ? (int) $id_raw : 0; if ($id <= 0) { return rb_rest_error('rb_invalid_id', 'Некорректный ID', 400); } $post = get_post($id); if (!($post instanceof WP_Post) || $post->post_type !== 'book') { return rb_rest_error('rb_not_found', 'Книга не найдена', 404); } $rating_meta = get_post_meta($id, 'rating', true); $rating = is_numeric($rating_meta) ? (float) $rating_meta : 0.0; if ($rating > 3.0) { return rb_rest_error('rb_forbidden', 'Cannot delete high-rated book', 403); } $deleted = wp_delete_post($id, true); if ($deleted === false) { return rb_rest_error('rb_delete_failed', 'Не удалось удалить запись', 500); } return rest_ensure_response(['deleted' => true, 'ID' => $id]); } catch (\Throwable $e) { return rb_handle_exception($e, 'rb_delete_book'); } } // [POST] /books/v1/ function rb_create_book($request) { try { if (!post_type_exists('book')) { return rb_rest_error('rb_post_type_missing', 'Post type "book" is not registered', 500); } // получение данных из запроса $data = $request->get_json_params(); if (!is_array($data) || empty($data)) { $data = $request->get_params(); } if (!is_array($data) || empty($data)) { return rb_rest_error('rb_no_data', 'No data provided', 400); } // опять валидация $validation = rb_validate_book_data($data, false); if ($validation !== true) { $message = is_array($validation) ? implode('; ', $validation) : 'Validation failed'; return rb_rest_error('rb_validation_error', $message, 400); } $title = isset($data['title']) ? sanitize_text_field($data['title']) : ''; $author = isset($data['author']) ? sanitize_text_field($data['author']) : ''; $content = isset($data['content']) ? wp_kses_post($data['content']) : ''; $publish_year = isset($data['publish_year']) && is_numeric($data['publish_year']) ? (int) $data['publish_year'] : null; $genres_input = isset($data['genres']) ? $data['genres'] : null; $genres = []; if ($genres_input !== null) { if (is_string($genres_input)) { $decoded = json_decode($genres_input, true); if (json_last_error() === JSON_ERROR_NONE) { $genres_input = $decoded; } } if (is_array($genres_input)) { foreach ($genres_input as $g) { if (is_string($g)) { $genres[] = sanitize_text_field($g); } } if (count($genres) > 3) { $genres = array_slice($genres, 0, 3); } } } // подсчёт рейтинга $genres_count = count($genres); $rating = rb_calculate_rating($publish_year !== null ? $publish_year : (int) date('Y'), $genres_count); if ($rating < 2.0) { return rb_rest_error('rb_low_rating', 'Рейтинг должен быть не меньше 2', 400); } $postarr = [ 'post_title' => $title, 'post_content' => $content, 'post_type' => 'book', 'post_status' => 'publish', ]; $post_id = wp_insert_post($postarr, true); if (is_wp_error($post_id)) { return rb_rest_error('rb_insert_failed', $post_id->get_error_message(), 500); } if ($author !== '') { update_post_meta($post_id, 'author', $author); } if ($publish_year !== null) { update_post_meta($post_id, 'publish_year', $publish_year); } if (!empty($genres)) { update_post_meta($post_id, 'genres', $genres); } update_post_meta($post_id, 'rating', (float) $rating); $created = get_post($post_id); if (!($created instanceof WP_Post)) { return rb_rest_error('rb_fetch_failed', 'Не удалось получить созданный пост', 500); } $author_meta = get_post_meta($post_id, 'author', true); $rating_meta = get_post_meta($post_id, 'rating', true); $rating_out = is_numeric($rating_meta) ? (float) $rating_meta : 0.0; $publish_year_meta = get_post_meta($post_id, 'publish_year', true); $publish_year_out = is_numeric($publish_year_meta) ? (int) $publish_year_meta : null; $rarity = null; if ($publish_year_out !== null && $publish_year_out < 1950) { $rarity = 'Раритет'; } // формирование и возврат ответа $response = [ 'ID' => $created->ID, 'title' => $created->post_title, 'author' => is_string($author_meta) ? $author_meta : '', 'rating' => $rating_out, ]; if ($publish_year_out !== null) { $response['publish_year'] = $publish_year_out; } if ($rarity !== null) { $response['rarity'] = $rarity; } $genres_meta = get_post_meta($post_id, 'genres', true); if (is_array($genres_meta)) { $response['genres'] = array_values(array_map('strval', $genres_meta)); } // формат ответа: {"title": "string", // "author": "string", // "rating": "number", // "publish_year": "number", // "rarity": "string", // "genres": ["string"]} return rest_ensure_response($response); } catch (\Throwable $e) { return rb_handle_exception($e, 'rb_create_book'); } } add_action('init', 'rb_register_book_post_type'); add_action('init', 'rb_register_book_meta'); add_action('rest_api_init', function () { register_rest_route('books', 'v1', [ 'methods' => ['GET', 'POST'], 'callback' => 'rb_books_handler', 'permission_callback' => 'rb_check_authentication' ]); register_rest_route('books/v1', '/(?P<id>\d+)', [ [ 'methods' => 'GET', 'callback' => 'rb_get_book_by_id', 'permission_callback' => 'rb_check_authentication', 'args' => [ 'id' => ['type' => 'integer', 'required' => true], ], ], [ 'methods' => 'PUT', 'callback' => 'rb_update_book', 'permission_callback' => 'rb_check_authentication', 'args' => [ 'id' => ['type' => 'integer', 'required' => true], ], ], [ 'methods' => 'DELETE', 'callback' => 'rb_delete_book', 'permission_callback' => 'rb_check_authentication', 'args' => [ 'id' => ['type' => 'integer', 'required' => true], ], ], ]); }); function rb_books_handler(WP_REST_Request $request) { $method = $request->get_method(); if ($method === 'GET') { return rb_get_books($request); } if ($method === 'POST') { return rb_create_book($request); } return new WP_Error('invalid_method', 'Метод не поддерживается', ['status' => 405]); }