I normally do this kind of things through the wp_get_nav_menu_items
filter. When I tried this first, it took me quite some time to get how I can mock up an existing WP_Post or WP_Term to be used as a nav menu item. You can’t just add the item itself, but you need to adapt it a little so that the nav item still links to the right destination.
Also, if you just add menu items it can mess up your menu order. So we have to rebuild the menu array with our own menu order counter.
I have used the following combination of a helper function and variations of the wp_get_nav_menu_items
filter in a couple of projects to quickly add new items to my menus, be it custom post types or taxonomies. It worked great so far. This approach should work whether you use the standard WP Nav Menu Walker or TimberMenu to display your menus in your theme.
The helper function
/**
* Prepare a post or term object to be used as a WP nav menu item.
*
* @param string $type The type of the object added to the menu. Can be 'page',
* 'category', 'taxonomy' or empty, assuming a custom post type
* @param WP_Post|WP_Term $menu_post The object you want to add that is converted
* to a menu item
* @param int $menu_parent The parent menu item you want to add the object to
* @param int $menu_order_counter The current menu order counter
* @param bool $set_new_id Whether to overwrite the current menu id. You normally want to
* do this, if you don’t want menu items to disappear randomly.
* @return void
*/
function pre_setup_nav_menu_item( $type = '', &$menu_post, $menu_parent, $menu_order_counter, $set_new_id = true ) {
$menu_post->menu_item_parent = $menu_parent;
$menu_post->menu_order = $menu_order_counter;
$menu_post->post_type = 'nav_menu_item';
if ( 'page' == $type ) {
$menu_post->object_id = $menu_post->ID;
$menu_post->object = 'page';
$menu_post->type = 'post_type';
} else if ( 'category' == $type ) {
$menu_post->object_id = $menu_post->term_id;
$menu_post->object = 'category';
$menu_post->type = 'taxonomy';
} else if ( 'taxonomy' == $type ) {
$menu_post->object_id = $menu_post->term_id;
$menu_post->object = $menu_post->taxonomy;
$menu_post->type = 'taxonomy';
// Assuming a custom post type
} else {
// Use TimberPost if Timber exists
if ( class_exists( 'Timber' ) ) {
$menu_post = new TimberPost( $menu_post );
}
$menu_post->object_id = $menu_post->ID;
$menu_post->object = $type;
$menu_post->type = 'post_type';
}
/**
* Create unique ID because in some cases the ID
* will act as an array key. This way, no menu objects
* should be overwritten.
*/
if ( $set_new_id ) {
$menu_post->ID = uniqid();
}
}
The filter
We take all the existing menu items and loop over them. When the menu item "Victims" is found, we get all the Victim Group terms and add them as submenu items. To be able to find the right menu item, you would create a page "Victims" which you then add manually to your Menu. In the following filter, you will have to set the page id of the page "Victim" as well as the name with which you registered your custom taxonomy.
/**
* Filter through nav menu items and add child items and anchor links accordingly
*/
add_filter( 'wp_get_nav_menu_items', function( $items, $menu, $args ) {
/**
* The page id of the page that was added manually to the menu
* and should hold the custom taxonomy menu items.
*/
$victim_page_id = 22;
// Name of the custom taxonomy victim groups
$victim_groups_tax_name = 'victimgroup';
/**
* Menus in Admin would also be affected if we wouldn’t
* check if we’re on the frontend
*/
if ( ! is_admin() ) {
// Array to hold the newly built nav menu items array
$new_items = array();
// Integer to store custom menu order as we build up the new array
$menu_order_counter = 1;
/**
* Loop through existing menu items and add them to custom menu
*/
foreach ( $items as $item ) {
// Add items in normal order
$item->menu_order = $menu_order_counter;
$new_items[] = $item;
$menu_order_counter++;
// Check for the menu item we want to add our submenu items to
if ( (int) $item->object_id == $victim_page_id ) {
// Victim Groups take the current menu item as a parent
$menu_parent = $item->ID;
// Get victim groups taxonomy terms
$terms = Timber::get_terms( $victim_groups_tax_name, array(
// You probably want to hide empty taxonomies
'hide_empty' => true,
) );
// Loop through terms found and add them as menu items
foreach ( $terms as $term ) {
$term->post_title = $term->name;
$term->post_parent = $term->parent;
pre_setup_nav_menu_item( 'taxonomy', $term, $menu_parent, $menu_order_counter );
$term = new TimberTerm( $term );
$new_items[] = wp_setup_nav_menu_item( $term );
$menu_order_counter++;
unset( $term );
}
}
}
return $new_items;
}
return $items;
}, 10, 3);