Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

Through API functions, I want to define a new Navigation menu, select it in the current theme, and then insert a few Pages as menu items. This is to be done for instance on a theme activation.

Through a (moderately painful) process of reverse engineering the database inserts and updates after manually setting up the Navigation menu and items, I've pieced together the following steps, where 'footer-nav' is the slug ID of the Navigation menu I'm creating:

if (!term_exists('footer-nav', 'nav_menu')) {

    $menu = wp_insert_term('Footer nav', 'nav_menu', array('slug' => 'footer-nav'));

    // Select this menu in the current theme
    update_option('theme_mods_'.get_current_theme(), array("nav_menu_locations" => array("primary" => $menu['term_id'])));

    // Insert new page
    $page = wp_insert_post(array('post_title' => 'Blog',
                                 'post_content' => '',
                                 'post_status' => 'publish',
                                 'post_type' => 'page'));

    // Insert new nav_menu_item
    $nav_item = wp_insert_post(array('post_title' => 'News',
                                     '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');
}

This seems to work, but:

  • is it a robust and elegant way of doing it?
  • am I missing something totally obvious that would do all this in one line of code?
share|improve this question

3 Answers 3

up vote 21 down vote accepted

I might be misunderstanding you, but why not use wp_create_nav_menu()?

E.g., this is what I do to create a custom BuddyPress menu when I detect BP as active:

    $menuname = $lblg_themename . ' BuddyPress Menu';
$bpmenulocation = 'lblgbpmenu';
// Does the menu exist already?
$menu_exists = wp_get_nav_menu_object( $menuname );

// If it doesn't exist, let's create it.
if( !$menu_exists){
    $menu_id = wp_create_nav_menu($menuname);

    // Set up default BuddyPress links and add them to the menu.
    wp_update_nav_menu_item($menu_id, 0, array(
        'menu-item-title' =>  __('Home'),
        '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' =>  __('Activity'),
        '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' =>  __('Members'),
        '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' =>  __('Groups'),
        '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' =>  __('Forums'),
        'menu-item-classes' => 'forums',
        'menu-item-url' => home_url( '/forums/' ), 
        'menu-item-status' => 'publish'));

    // Grab the theme locations and assign our newly-created menu
    // to the BuddyPress menu location.
    if( !has_nav_menu( $bpmenulocation ) ){
        $locations = get_theme_mod('nav_menu_locations');
        $locations[$bpmenulocation] = $menu_id;
        set_theme_mod( 'nav_menu_locations', $locations );
    }
share|improve this answer
    
I didn't know about this function. Yes, I guess it will make the above code much shorter. I guess I should move beyond the Codex and dive into the actual code, as I find that the API functions are often, like in this case, too low-level. Thanks! –  julien_c Mar 7 '12 at 14:18
    
@julien_c if this is solved, mark it as such to allow those who come after you to benefit from your experience here. –  m0r7if3r Mar 7 '12 at 15:07
    
I just want to actually test it in real life so I'm sure it does what I want. I'll remember to mark it as solved as soon as I'm done! –  julien_c Mar 7 '12 at 15:51
2  
If you see useful functions like these that aren't in the codex, it's a good idea to add them (yay wiki) =p –  Tom J Nowell Mar 9 '12 at 19:51
    
Sorry it took me such a long time to check that it worked in my case. Answer accepted! Also, you're defining custom links menu items, I've added an answer below to define page links (which will be more robust to URL changes, for example). –  julien_c May 15 '12 at 11:06

As a complement to ZaMoose's anwser, here's how you would create a "Page-type" menu item (not a "Custom" one):

wp_update_nav_menu_item($menu_id, 0, array('menu-item-title' => 'About',
                                           'menu-item-object' => 'page',
                                           'menu-item-object-id' => get_page_by_path('about')->ID,
                                           'menu-item-type' => 'post_type',
                                           'menu-item-status' => 'publish'));

Assuming you only know the page slug, for instance.

share|improve this answer

I have a few issues with the accepted answer - that doesn't make it wrong, but I'll post my own code below which I feel might have a better result for some people since I had the same question but wanted to do the same thing with less code.

First, the above code creates "URL" type navigation items, which is fine for some people but I want to link to PAGES, not URLs because this is an important feature of WordPress navigations and clients inevitable move things around so I never use the URL nav item type.

Also, only a flat array of children is handled by the posted code. I've created a function for recursively declaring the new nav items, storing their returned metadata (mainly ID after being created in the loop), and a parameter for accepting children.

Just edit $nav_items_to_add and the rest is handled recursively. There are 3 required keys in each array. First, the array key is the slug, so 'shop' => array( ... ) is what you want for a page with the slug shop. ['title']is the way the nav item will be labeled on the front end. path is the path to the page within the WordPress page hierarchy, so this is identical to the slug if the page is a top-level parent, and if shop were a child of home then it would be 'path' => 'home/shop'.

The last optional array key is ['parent'] where you can declare another key in the array as the parent of the current one. It's important to note that the items are added recursively, so the parent has to exist before you try to create a child. This means the declaration should happen for the parent nav item before it's children.

    $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' => 'Shop',
                    'path' => 'shop',
                    ),
                'shop_l2' => array(
                    'title' => 'Shop',
                    'path' => 'shop',
                    'parent' => 'shop',
                    ),
                'cart' => array(
                    'title' => 'Cart',
                    'path' => 'shop/cart',
                    'parent' => 'shop',
                    ),
                'checkout' => array(
                    'title' => 'Checkout',
                    'path' => 'shop/checkout',
                    'parent' => 'shop',
                    ),
                'my-account' => array(
                    'title' => 'My Account',
                    'path' => 'shop/my-account',
                    'parent' => 'shop',
                    ),
                'lost-password' => array(
                    'title' => 'Lost Password',
                    'path' => 'shop/my-account/lost-password',
                    'parent' => 'my-account',
                    ),
                'edit-address' => array(
                    'title' => 'Edit My Address',
                    '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')
        );
    }

    }
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.