wp_get_nav_menu_items() не работает с ярлыком (slug) в WordPress
Согласно документации, первый параметр функции wp_get_nav_menu_items()
принимает:
(string) (Обязательный) Название меню, ID или ярлык (slug).
Вот как я регистрирую свои меню:
register_nav_menus(
array(
'primary' => 'Navigation primaire',
'secondary' => 'Navigation secondaire',
)
);
Вот как я пытаюсь загрузить меню:
var_dump( wp_get_nav_menu_items('primary') ); // bool(false)
var_dump( wp_get_nav_menu_items('navigation-primaire') ); // bool(false)
var_dump( wp_get_nav_menu_items('Navigation primaire') ); // bool(false)
var_dump( wp_get_nav_menu_items(51) ); // array([...]) (корректно)
Функция возвращает меню только когда я использую ID. Я бы предпочел использовать ярлык (slug), так как мой сайт многоязычный и я не хочу использовать разные ID для разных языков.
Я делаю что-то не так или это баг в ядре WordPress? Я пробовал это в теме TwentySixteen с отключенным мультиязычным плагином (Polylang).

Поскольку никто толком не объяснил, почему функция не работает так, как вы ожидали, я попробую подробно разобрать этот момент, так как сам недавно попал в ту же ловушку.
Проблема в том, что документация не совсем четко объясняет, к чему относится параметр slug. Большинство предполагает, что это slug зарегистрированного меню, но это неверно. На самом деле, это slug термина для таксономии nav_menu.
Давайте углубимся в исходный код, чтобы увидеть, как WordPress ядро интерпретирует slug меню.
function wp_get_nav_menu_items( $menu, $args = array() ) {
$menu = wp_get_nav_menu_object( $menu );
....
Первая строка показывает, что используется функция wp_get_nav_menu_object() для получения меню. В нее передается параметр $menu
, который мы используем в качестве slug. Поэтому нам нужно изучить исходный код функции wp_get_nav_menu_object()
.
function wp_get_nav_menu_object( $menu ) {
$menu_obj = false;
if ( is_object( $menu ) ) {
$menu_obj = $menu;
}
if ( $menu && ! $menu_obj ) {
$menu_obj = get_term( $menu, 'nav_menu' );
if ( ! $menu_obj ) {
$menu_obj = get_term_by( 'slug', $menu, 'nav_menu' );
}
if ( ! $menu_obj ) {
$menu_obj = get_term_by( 'name', $menu, 'nav_menu' );
}
}
....
Мы видим, что эта функция использует get_term_by
для получения объекта $menu_obj
. Именно здесь определяется наше меню.
Когда вы создаете меню в админке WordPress, система создает новый термин. Термины имеют slug на основе их названия. Например, если меню называется "Мое Крутое Меню", WordPress сгенерирует slug "мое-крутое-меню". Вы могли зарегистрировать меню как "primary", но это не тот slug, к которому обращаются эти функции. Речь идет именно о slug термина меню.
Чтобы было понятнее, допустим, вы зарегистрировали меню так:
register_nav_menu('primary', 'Мое основное меню');
В этом случае primary
— это не slug. мое-основное-меню
— тоже не slug.
Если мы создали меню с названием "Мое Крутое Меню" (для примера), то его slug будет 'мое-крутое-меню', и получить его можно так:
$awesome_menu = wp_get_nav_menu_items( 'мое-крутое-меню' );
Вы можете проверить slug, посмотрев в таблицу wp_terms (или с вашим префиксом) и найдя там название вашего меню.
Если у вас мультиязычный сайт или мультисайт, этот метод не будет оптимальным, так как название и slug могут отличаться.
Поэтому лучший способ — получить все расположения меню с помощью get_nav_menu_locations()
, которая вернет ассоциативный массив, где ключ — это slug зарегистрированного меню, а значение — ID термина меню, выбранного для этого расположения.
С ID термина мы можем получить информацию о термине и затем вернуть правильный slug, который требует функция.
Ниже приведена функция, которая использует wp_get_nav_items()
, но вы передаете в нее slug зарегистрированного меню. В вашем случае это будет 'primary'.
Решение в коде
// название функции длинное, но не суть
function get_menu_items_by_registered_slug($menu_slug) {
$menu_items = array();
if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_slug ] ) ) {
$menu = get_term( $locations[ $menu_slug ] );
$menu_items = wp_get_nav_menu_items($menu->term_id);
}
return $menu_items;
}

Небольшая модификация принятого ответа
После удаления меню из расположения, $locations[ $menu_slug ]
возвращает 0
, поэтому хорошо добавить эту проверку:
function get_menu_items_by_registered_slug($menu_slug) {
$menu_items = array();
if ( ($locations = get_nav_menu_locations()) && isset($locations[$menu_slug]) && $locations[$menu_slug] != 0 ) {
$menu = get_term( $locations[ $menu_slug ] );
$menu_items = wp_get_nav_menu_items($menu->term_id);
}
return $menu_items;
}

Попробуйте так:
$menu_name = 'primary'; // Имя меню
if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) {
$menu = wp_get_nav_menu_object( $locations[ $menu_name ] ); // Получаем объект меню
$menu_items = wp_get_nav_menu_items($menu->term_id); // Получаем элементы меню
// Ваш код для вывода
}

У меня не срабатывает. get_nav_menu_locations()
возвращает массив с одним элементом ('primary' => 51
), но wp_get_nav_menu_items()
возвращает false при использовании слага.

custom_menu('primary');
function custom_menu( $theme_location ) {
if ( ($theme_location) && ($locations = get_nav_menu_locations()) && isset($locations[$theme_location]) ) {
$menu = get_term( $locations[$theme_location], 'nav_menu' );
$menu_items = wp_get_nav_menu_items($menu->term_id);
//ваш код меню.
}
