Получение меню навигации WordPress через REST API V2
Я пытаюсь получить навигационное меню из JSON-ответа, используя плагин WP REST API v2.
Для REST API v2 нет расширения плагина навигационного меню, оно есть только для V1.
Из документации WordPress Post Types я узнал, что навигационное меню обрабатывается как тип записи.
Из документации Rest API, вот как мы получаем записи определенного типа:
GET http://demo.wp-api.org/wp-json/wp/v2/types/<type>
Я попытался получить его таким образом:
URL : http://localhost/wptest/wp-json/wp/v2/types/nav_menu_item
Я получил ошибку 403.
{"code":"rest_cannot_read_type","message":"Cannot view type.","data":{"status":403}}
Сервер понял мой запрос, но отказался предоставить данные.
Вопрос: Как это исправить?

Поскольку мне самому не нравится, когда лучший ответ - "Установите плагин X", вот как я решил эту проблему:
Меню в настоящее время недоступны в WP REST API. Поэтому вам нужно зарегистрировать собственный пользовательский endpoint, а затем просто вызывать этот маршрут из вашего приложения, которому это необходимо.
Вы можете добавить что-то вроде этого (в functions.php, плагин или где угодно):
function get_menu() {
# Измените 'menu' на ваш собственный слаг навигации.
return wp_get_nav_menu_items('menu');
}
add_action( 'rest_api_init', function () {
register_rest_route( 'myroutes', '/menu', array(
'methods' => 'GET',
'callback' => 'get_menu',
) );
} );
Для приведенного выше примера вы сможете получить данные по адресу:
http://your-domain.dev/wp-json/myroutes/menu
Вы можете использовать этот метод для создания любых маршрутов, чтобы получать любые данные, которые недоступны в WP REST API. Также это полезно, если вам нужно обработать какие-то данные перед отправкой в ваше приложение.

спасибо, что поделились своим решением, а не просто кинули ссылку на плагин ;-) Однако лучше было бы добавить префикс к названиям ваших функций или использовать пространство имен, чтобы избежать возможных конфликтов, так как get_menu()
- довольно общее название.

Потрясающе, люди не понимают, что у большинства уже установлено от 30 до 70 плагинов. У них даже есть плагины для деактивации других плагинов! Это безумие. Думаю, я установлю плагин, чтобы сохранить эту ветку.

@moeses просто убедитесь, что название меню в настройках меню в wp-admin совпадает с названием, которое вы передаёте в функцию wp_get_nav_menu_items в качестве параметра

Ответ от @Liren работает хорошо. Однако некоторым новичкам может быть сложно настроить маршрут. Вот код, который хорошо работает с WordPress Rest API v2 с минимальными изменениями.
Замените название вашего меню только в функции wp_get_nav_menu_items(). Если название меню и его ярлык не работают (возвращают false), используйте ID меню (виден в админке при редактировании этого меню).
function get_my_menu() {
// Внимательно замените название меню, его ярлык или ID
return wp_get_nav_menu_items('Основная навигация');
}
add_action( 'rest_api_init', function () {
register_rest_route( 'wp/v2', 'menu', array(
'methods' => 'GET',
'callback' => 'get_my_menu',
) );
} );
URL маршрута:
https://website.com/wp-json/wp/v2/menu
Подробнее рассмотрено в уроке: WordPress Rest API – Получение элементов навигационного меню

Существует расширение плагина для навигационного меню в REST API v2: https://wordpress.org/plugins/wp-api-menus/

Я выбрал самый простой способ сделать это, сохраняя при этом динамичность, чтобы можно было получить более одного меню.
/**
* Возвращает элементы меню в виде массива на основе переданного ID навигационного меню
*
* @param object Запрос, из которого можно получить параметры.
* @return array Элементы меню, содержащиеся в указанном меню
*/
function expose_navigation($request) {
$id = $request['id'];
return wp_get_nav_menu_items($id);
}
/**
* Предоставляет доступ к элементам меню через WP REST API по пути /navigation/{id}
*
* @return void
*/
function expose_navigation_to_rest() {
register_rest_route( 'wp/v2', '/navigation/(?P<id>\d+)', [
'methods' => 'GET',
'callback' => 'expose_navigation'
]
);
}
add_action('rest_api_init', 'expose_navigation_to_rest');
Таким образом, меню можно легко запросить по пути /navigation/{id}

При регистрации типа записи необходимо добавить параметр 'show_in_rest' => true,
.
Подробнее можно узнать по ссылке http://v2.wp-api.org/extending/custom-content-types/

register_post_type('nav_menu_item', array('show_in_rest' => true));

Я согласен с ответом @Lirens, но меню следует вызывать по ID, а не по слагу. Также слеш перед путем к меню не нужен. В итоге получается что-то вроде этого:
function get_menu() {
# Замените '2' на ID вашего меню.
return wp_get_nav_menu_items(2);
}
add_action( 'rest_api_init', function () {
register_rest_route( 'myroutes', 'menu', array(
'methods' => 'GET',
'callback' => 'get_menu',
) );
} );
В таком виде у меня сработало.

Я не думаю, что для таких задач следует использовать плагин. Также ответ hkc на самом деле не так уж плох, ему просто нужно дополнительное объяснение, чтобы заставить это работать с типом записи nav_menu_item
(который используется для навигационных меню WordPress).
Этот тип записи уже зарегистрирован, поэтому нам нужно его изменить. Это легко сделать, подключившись к фильтру register_post_type_args
. Этот фильтр позволяет изменить аргументы для конкретного типа записи. В приведённом ниже коде показано, как это сделать для типа записи nav_menu_item
.
add_filter('register_post_type_args', function ($args, $post_type) {
if ($post_type == 'nav_menu_item' &&
class_exists('WP_REST_Posts_Controller') &&
!class_exists('WP_REST_NavMenuItem_Controller')) {
class WP_REST_NavMenuItem_Controller extends WP_REST_Posts_Controller {
public function get_items( $request ) {
$args = wp_parse_args($request, [
'order' => 'ASC',
'orderby' => 'menu_order',
]);
$output = [];
if (empty($request['menu'])) {
$menus = get_registered_nav_menus();
foreach ( $menus as $location => $description ) {
$items = wp_get_nav_menu_items($location, $args);
$output = array_merge($output, is_array($items) ? $items : []);
}
} else {
$items = wp_get_nav_menu_items($request['menu'], $args);
$output = array_merge($output, is_array($items) ? $items : []);
}
return rest_ensure_response($output);
}
public function get_collection_params() {
$query_params = parent::get_collection_params();
$query_params['menu'] = [
'description' => __( 'Имя или theme_location меню' ),
'type' => 'string',
];
return $query_params;
}
}
// Изменяем аргументы типа записи
$args['show_in_rest'] = true;
$args['rest_controller_class'] = 'WP_REST_NavMenuItem_Controller';
}
return $args;
}, 10, 2);
Как вы могли заметить из приведённого выше кода, он делает немного больше, чем просто отображает тип записи в REST. Он также изменяет стандартный REST-контроллер записей, чтобы выводить в REST результат, похожий на описанный в ответе Liren. Помимо этого, он также выполняет все функции, которые выполняют REST-контроллеры типов записей, давая вам больше контроля и функциональности. Также стоит отметить, что это более стабильный вариант, так как он не конфликтует с другими REST-маршрутами, и, что не менее важно, с ним гораздо удобнее работать.

Вы можете получить все зарегистрированные меню и их данные.
Обновленный код:
function get_menu() {
# Замените 'menu' на ваш собственный слаг навигации.
$menus = wp_get_nav_menus();
$menuArray = [];
foreach($menus as $key=>$menu_term){
$menuArray[] = nav_sort_menu_items(wp_get_nav_menu_items($menu_term->term_id));
}
return $menuArray;
}
add_action( 'rest_api_init', function () {
register_rest_route( 'route', '/menu', array(
'methods' => 'GET',
'callback' => 'get_menu',
) );
} );
Затем вы можете получать данные из
