Программное добавление навигационного меню и пунктов меню
С помощью API-функций я хочу определить новое навигационное меню, выбрать его в текущей теме, а затем добавить несколько страниц как пункты меню. Это должно выполняться, например, при активации темы.
Через (относительно сложный) процесс обратной разработки вставок и обновлений базы данных после ручной настройки навигационного меню и элементов, я собрал следующие шаги, где 'footer-nav' - это slug ID создаваемого навигационного меню:
if (!term_exists('footer-nav', 'nav_menu')) {
// Создаем новое меню
$menu = wp_insert_term('Нижнее меню', 'nav_menu', array('slug' => 'footer-nav'));
// Выбираем это меню в текущей теме
update_option('theme_mods_'.get_current_theme(), array("nav_menu_locations" => array("primary" => $menu['term_id'])));
// Вставляем новую страницу
$page = wp_insert_post(array('post_title' => 'Блог',
'post_content' => '',
'post_status' => 'publish',
'post_type' => 'page'));
// Вставляем новый элемент меню
$nav_item = wp_insert_post(array('post_title' => 'Новости',
'post_content' => '',
'post_status' => 'publish',
'post_type' => 'nav_menu_item'));
add_post_meta($nav_item, '_menu_item_type', 'post_type');
add_post_meta($nav_item, '_menu_item_menu_item_parent', '0');
add_post_meta($nav_item, '_menu_item_object_id', $page);
add_post_meta($nav_item, '_menu_item_object', 'page');
add_post_meta($nav_item, '_menu_item_target', '');
add_post_meta($nav_item, '_menu_item_classes', 'a:1:{i:0;s:0:"";}');
add_post_meta($nav_item, '_menu_item_xfn', '');
add_post_meta($nav_item, '_menu_item_url', '');
wp_set_object_terms($nav_item, 'footer-nav', 'nav_menu');
}
Это вроде бы работает, но:
- является ли это надежным и элегантным способом?
- не упускаю ли я что-то совершенно очевидное, что могло бы сделать все это в одну строку кода?
Возможно, я вас неправильно понимаю, но почему бы не использовать wp_create_nav_menu()
?
Например, вот что я делаю для создания пользовательского меню BuddyPress, когда обнаруживаю, что BP активен:
$menuname = $lblg_themename . ' BuddyPress Menu';
$bpmenulocation = 'lblgbpmenu';
// Существует ли меню уже?
$menu_exists = wp_get_nav_menu_object( $menuname );
// Если меню не существует, создадим его.
if( !$menu_exists){
$menu_id = wp_create_nav_menu($menuname);
// Устанавливаем стандартные ссылки BuddyPress и добавляем их в меню.
wp_update_nav_menu_item($menu_id, 0, array(
'menu-item-title' => __('Главная'),
'menu-item-classes' => 'home',
'menu-item-url' => home_url( '/' ),
'menu-item-status' => 'publish'));
wp_update_nav_menu_item($menu_id, 0, array(
'menu-item-title' => __('Активность'),
'menu-item-classes' => 'activity',
'menu-item-url' => home_url( '/activity/' ),
'menu-item-status' => 'publish'));
wp_update_nav_menu_item($menu_id, 0, array(
'menu-item-title' => __('Участники'),
'menu-item-classes' => 'members',
'menu-item-url' => home_url( '/members/' ),
'menu-item-status' => 'publish'));
wp_update_nav_menu_item($menu_id, 0, array(
'menu-item-title' => __('Группы'),
'menu-item-classes' => 'groups',
'menu-item-url' => home_url( '/groups/' ),
'menu-item-status' => 'publish'));
wp_update_nav_menu_item($menu_id, 0, array(
'menu-item-title' => __('Форумы'),
'menu-item-classes' => 'forums',
'menu-item-url' => home_url( '/forums/' ),
'menu-item-status' => 'publish'));
// Получаем расположения меню в теме и назначаем только что созданное меню
// в расположение меню BuddyPress.
if( !has_nav_menu( $bpmenulocation ) ){
$locations = get_theme_mod('nav_menu_locations');
$locations[$bpmenulocation] = $menu_id;
set_theme_mod( 'nav_menu_locations', $locations );
}

Я не знал об этой функции. Да, думаю, это значительно сократит приведённый выше код. Наверное, мне стоит выйти за рамки Codex и погрузиться в сам код, так как я замечаю, что API-функции часто, как в данном случае, оказываются слишком низкоуровневыми. Спасибо!

@julien_c если проблема решена, отметьте её как решённую, чтобы те, кто придёт после вас, могли воспользоваться вашим опытом.

Я просто хочу сначала протестировать это в реальных условиях, чтобы убедиться, что всё работает как нужно. Обязательно отмечу как решённое, как только закончу!

Если вы видите полезные функции, подобные этим, которых нет в кодексе, хорошей идеей будет добавить их (ура, вики) =p

Извините, что у меня заняло так много времени проверить, работает ли это в моем случае. Ответ принят! Также, вы определяете пункты меню как пользовательские ссылки, я добавил ответ ниже для определения ссылок на страницы (что будет более надежным при изменении URL, например).

http://hookr.io/3.9.1/actions/do_action/wp_create_nav_menu/

У меня есть несколько замечаний к принятому ответу — это не делает его неправильным, но я опубликую ниже свой код, который, как мне кажется, может дать лучший результат для некоторых пользователей, так как у меня был тот же вопрос, но я хотел сделать то же самое с меньшим количеством кода.
Во-первых, приведённый выше код создаёт элементы навигации типа "URL", что подходит некоторым, но я хочу ссылаться на СТРАНИЦЫ, а не на URL, потому что это важная особенность навигации в WordPress, и клиенты неизбежно перемещают элементы, поэтому я никогда не использую тип элемента навигации "URL".
Кроме того, опубликованный код обрабатывает только плоский массив дочерних элементов. Я создал функцию для рекурсивного объявления новых элементов навигации, хранения их возвращаемых метаданных (в основном ID после создания в цикле) и параметр для принятия дочерних элементов.
Просто отредактируйте $nav_items_to_add
, а остальное обрабатывается рекурсивно. В каждом массиве есть 3 обязательных ключа. Во-первых, ключ массива — это slug, поэтому 'shop' => array( ... )
— это то, что вам нужно для страницы с slug shop
. ['title']
— это название элемента навигации, которое будет отображаться на фронтенде. path
— это путь к странице в иерархии страниц WordPress, поэтому он идентичен slug, если страница является родительской верхнего уровня, а если shop
является дочерней для home
, тогда это будет 'path' => 'home/shop'
.
Последний необязательный ключ массива — ['parent']
, где вы можете указать другой ключ в массиве как родительский для текущего. Важно отметить, что элементы добавляются рекурсивно, поэтому родитель должен существовать до попытки создания дочернего элемента. Это означает, что объявление родительского элемента навигации должно происходить раньше, чем его дочерних элементов.
$locations = get_nav_menu_locations();
if (isset($locations['primary_navigation'])) {
$menu_id = $locations['primary_navigation'];
$new_menu_obj = array();
$nav_items_to_add = array(
'shop' => array(
'title' => 'Магазин',
'path' => 'shop',
),
'shop_l2' => array(
'title' => 'Магазин',
'path' => 'shop',
'parent' => 'shop',
),
'cart' => array(
'title' => 'Корзина',
'path' => 'shop/cart',
'parent' => 'shop',
),
'checkout' => array(
'title' => 'Оформление заказа',
'path' => 'shop/checkout',
'parent' => 'shop',
),
'my-account' => array(
'title' => 'Мой аккаунт',
'path' => 'shop/my-account',
'parent' => 'shop',
),
'lost-password' => array(
'title' => 'Забыли пароль',
'path' => 'shop/my-account/lost-password',
'parent' => 'my-account',
),
'edit-address' => array(
'title' => 'Изменить адрес',
'path' => 'shop/my-account/edit-address',
'parent' => 'my-account',
),
);
foreach ( $nav_items_to_add as $slug => $nav_item ) {
$new_menu_obj[$slug] = array();
if ( array_key_exists( 'parent', $nav_item ) )
$new_menu_obj[$slug]['parent'] = $nav_item['parent'];
$new_menu_obj[$slug]['id'] = wp_update_nav_menu_item($menu_id, 0, array(
'menu-item-title' => $nav_item['title'],
'menu-item-object' => 'page',
'menu-item-parent-id' => $new_menu_obj[ $nav_item['parent'] ]['id'],
'menu-item-object-id' => get_page_by_path( $nav_item['path'] )->ID,
'menu-item-type' => 'post_type',
'menu-item-status' => 'publish')
);
}
}

В дополнение к ответу ZaMoose, вот как можно создать пункт меню типа "Страница" (а не "Произвольный"):
wp_update_nav_menu_item($menu_id, 0, array('menu-item-title' => 'О нас',
'menu-item-object' => 'page',
'menu-item-object-id' => get_page_by_path('about')->ID,
'menu-item-type' => 'post_type',
'menu-item-status' => 'publish'));
Предполагается, что вам известен только slug страницы, например.

Чтобы добавить пункт меню программно, вы можете использовать фильтр wp_nav_menu_items
. Разместите следующий код в файле functions.php вашей темы, чтобы добавить пункт меню для входа/выхода в главное меню. 'Primary' — это название/идентификатор зарегистрированного меню.
/**
* Добавление пункта меню для входа/выхода в главное меню.
* ======================================================
*/
add_filter( 'wp_nav_menu_items', 'lunchbox_add_loginout_link', 10, 2 );
function lunchbox_add_loginout_link( $items, $args ) {
/**
* Если установлено основное меню и пользователь авторизован.
*/
if ( is_user_logged_in() && $args->theme_location == 'primary' ) {
$items .= '<li><a href="'. wp_logout_url() .'">Выйти</a></li>';
}
/**
* Иначе отобразить пункт меню для входа.
*/
elseif ( !is_user_logged_in() && $args->theme_location == 'primary' ) {
$items .= '<li><a href="'. site_url('wp-login.php') .'">Войти</a></li>';
}
return $items;
}
