How to Automatically Create WordPress Navigation Menu Items
Let’s say you’ve got a custom theme (or a modified child theme) and you’re already registering navigation menus. Now you want to also automatically add the actual menu items to those navigation menus. Maybe you’ve got a Multisite install and every time someone creates a new site, you want their navigation all setup for them.
This is totally possible and I spent the morning figuring all of the intricacies out, thanks of course to some great support from the WordPress Codex and Stack Exchange.
Firstly, you register the nav menus in your functions.php file, something like this:
// Create Nav Menus
register_nav_menus( array(
'primary' => 'Primary',
) );
I’ve only registered one here, but you can register multiple like so:
// Create Nav Menus
register_nav_menus( array(
'primary' => 'Primary',
'secondary' => 'Secondary',
'mobile' => 'Mobile'
) );
Next, we want to create all of the navigation items for the Primary menu. In the example below we’ll create both custom menu links, as well as automatically add some categories and make them sub-navigation items of a custom “Topics” item.
// setup navigation automatically
add_action('load-nav-menus.php', 'auto_nav_creation_primary');
function auto_nav_creation_primary(){
$name = 'Navigation';
$menu_exists = wp_get_nav_menu_object($name);
if( !$menu_exists){
$menu_id = wp_create_nav_menu($name);
$menu = get_term_by( 'name', $name, 'nav_menu' );
wp_update_nav_menu_item($menu->term_id, 0, array(
'menu-item-title' => __('Topics'),
'menu-item-classes' => 'topics-dropdown',
'menu-item-url' => '#',
'menu-item-type' => 'custom',
'menu-item-status' => 'publish'));
$cat_args=array(
'exclude' => '1,10',
'hide_empty' => 0
);
$the_cats = get_categories($cat_args);
if (count($the_cats) > 0){
foreach($the_cats as $category){
wp_update_nav_menu_item($menu->term_id, 0, array(
'menu-item-title' => $category->name,
'menu-item-object-id' => $category->term_id,
'menu-item-db-id' => 0,
'menu-item-object' => 'category',
'menu-item-parent-id' => 5,
'menu-item-depth' => 1,
'menu-item-type' => 'taxonomy',
'menu-item-url' => get_category_link($category->term_id),
'menu-item-status' => 'publish',)
);
}
}
$event_cat_args=array(
'include' => '10',
'hide_empty' => 0
);
$eventCat = get_categories($event_cat_args);
if (count($eventCat) > 0){
foreach($eventCat as $eventCat){
wp_update_nav_menu_item($menu->term_id, 0, array(
'menu-item-title' => $eventCat->name,
'menu-item-object-id' => $eventCat->term_id,
'menu-item-db-id' => 0,
'menu-item-object' => 'category',
'menu-item-type' => 'taxonomy',
'menu-item-url' => get_category_link($eventCat->term_id),
'menu-item-status' => 'publish',)
);
}
}
}
//then you set the wanted theme location
$locations = get_theme_mod('nav_menu_locations');
$locations['primary'] = $menu->term_id;
set_theme_mod( 'nav_menu_locations', $locations );
}
There you have it! What this code does is exactly what I’ve specified in the previous paragraph, and it all happens whenever you visit the Appearance > Menu page.
But let’s explain what’s going on here in a bit more detail.
We’re setting this all up as a function, and that function is auto_nav_creation_primary
. In the line below, we add that function to the load-nav-menus.php
hook, which fires when the Appearance > Menus screen is loaded. You could change this to something else, but note that you need to have already created some categories for that part to work.
In my application, I programmatically create the categories, too, before I create these nav menus. I’ll post on that some other day.
add_action('load-nav-menus.php', 'auto_nav_creation_primary');
$name = 'Navigation';
$menu_exists = wp_get_nav_menu_object($name);
if( !$menu_exists){
$menu_id = wp_create_nav_menu($name);
$menu = get_term_by( 'name', $name, 'nav_menu' );
Here we define the name of our menu, and then using $menu_exists = wp_get_nav_menu_object($name);
and the following line, we check to see if the menu exists already. No reason to create or overwrite it if it does (otherwise, every time you visited this screen you would rewrite your menu, so no manual changes could ever be made).
Next, we’ll create the “Topics” top-level, custom menu item I’d mentioned. This code can be edited to create any navigation item, linking to any URL, and even give it a custom CSS class.
wp_update_nav_menu_item($menu->term_id, 0, array(
'menu-item-title' => __('Topics'), // the name of the menu item
'menu-item-classes' => 'topics-dropdown', // a custom CSS class
'menu-item-url' => '#', // the URL, could be replaced with 'get_bloginfo('url')' if you want the homepage URL, or get_permalink('55'), where 55 is the ID of a particular page, if you want to have it be a link to some specific post or page
'menu-item-type' => 'custom',
'menu-item-status' => 'publish'));
Next, we’ll load up our categories and add them as sub-nav items to this ‘Topics’ item. In this case, I don’t want the Uncategorized category, and I’ve excluded a category with an ID of 10, too, so that we can show that outside of this dropdown (more on that below).
$cat_args=array(
'exclude' => '1,10', // exclude 1 (the Uncategorized category) and 10, another category we don't want in this sub-menu
'hide_empty' => 0 // I use this to force WordPress to return even empty categories, which get_categories doesn't do by default
);
$the_cats = get_categories($cat_args); // get the list of categories from our arguments above
if (count($the_cats) > 0){ // if the list isn't empty, let's do this!
foreach($the_cats as $category){
wp_update_nav_menu_item($menu->term_id, 0, array(
'menu-item-title' => $category->name, // the visible name of the item will be the category's name
'menu-item-object-id' => $category->term_id,
'menu-item-db-id' => 0, // 0 equates to setting a new ID for this item
'menu-item-object' => 'category',
'menu-item-parent-id' => 5, // here I know that 5 is the 'Topics' menu item ID, replace as needed. This makes each category a child of the Topics item
'menu-item-depth' => 1, // this sets the depth to 1, though it may not be necessary
'menu-item-type' => 'taxonomy', // this defines the type of menu item, where we used Custom before, we're now defining it as a taxonomy
'menu-item-url' => get_category_link($category->term_id), // sets the URL for the menu item
'menu-item-status' => 'publish',)
);
}
}
You can do something similar to only retrieve one category. In this case, I want a particular category to live as a top level nav item.
$event_cat_args=array(
'include' => '10', // we include the category's ID instead of excluding some
'hide_empty' => 0
);
$eventCat = get_categories($event_cat_args);
if (count($eventCat) > 0){
foreach($eventCat as $eventCat){
wp_update_nav_menu_item($menu->term_id, 0, array(
'menu-item-title' => $eventCat->name,
'menu-item-object-id' => $eventCat->term_id,
'menu-item-db-id' => 0,
'menu-item-object' => 'category',
'menu-item-type' => 'taxonomy',
'menu-item-url' => get_category_link($eventCat->term_id),
'menu-item-status' => 'publish',)
);
}
}
All that’s left then is to assign these all to a specific nav menu (remember when we registered them way at the beginning of this post?)
//then you set the wanted theme location
$locations = get_theme_mod('nav_menu_locations');
$locations['primary'] = $menu->term_id;
set_theme_mod( 'nav_menu_locations', $locations );
}
There you go! No more manually setting up your navigation items, have WP do it for you.