Skip to content

Instantly share code, notes, and snippets.

@vwasteels
Last active June 28, 2022 13:12
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save vwasteels/874f7d08726076bdc580 to your computer and use it in GitHub Desktop.
Save vwasteels/874f7d08726076bdc580 to your computer and use it in GitHub Desktop.
Retrieve menu items hierarchically in Wordpress
/**
 * Get Menu Items From Location
 *
 * @param $location : location slug given as key in register_nav_menus
 */

function getMenuItemsFromLocation($location) {
	$theme_locations = get_nav_menu_locations();
	$menu_obj = get_term( $theme_locations[$location], 'nav_menu' );
	return is_wp_error($menu_obj) ? [] : getMenuItemsForParent($menu_obj->slug, 0)
}


/**
 * Get Menu Items For Parent
 * 
 * @param $menuSlug : menu slug for the CMS entry (not the key in register_nav_menus)
 * @param $parentId
 * @return array of items formatted as objects with : name / url / children (fetched recursively)
 */

function getMenuItemsForParent($menuSlug, $parentId) {
	$args = [
			'post_type' => 'nav_menu_item',
			'meta_key' => '_menu_item_menu_item_parent',
			'meta_value' => $parentId,
			'tax_query' => [
				[
					'taxonomy' => 'nav_menu',
					'field' => 'slug',
					'terms' => [$menuSlug]
				]
			],
			'order' => 'ASC',
			'orderby' => 'menu_order',
			'posts_per_page' => -1
		];
	$tmpItems = query_posts($args);

	$items = [];
	foreach ( $tmpItems as $tmpItem ) {
		$item = new stdClass;
		$type = get_post_meta($tmpItem->ID, '_menu_item_type', true);
		switch($type):

			case 'post_type':
				$postId = get_post_meta($tmpItem->ID, '_menu_item_object_id', true);
				$post = get_post($postId);
				$item->name = $post->post_title;
				$item->url = '/'.$post->post_type.'/'.$post->post_name;
				break;

			case 'custom':
				$item->name = $tmpItem->post_title;
				$item->url = get_post_meta($tmpItem->ID, '_menu_item_url', true);
				
			// note : this has to be completed with every '_menu_item_type' (could also come from plugin)

		endswitch;
		$item->children = getMenuItemsForParent($menuSlug, $tmpItem->ID);
		$items[] = $item;
	}

	return $items;
}

/**
 * Usage
 */
 
$menuItems = getMenuItemsFromLocation('footer_menu');

// will return :
[
	'name' => 'Menu item 1'
	'url' => '/my-post-type/my-url-1'
	'children' => [
		'name' => 'Menu item 2'
		'url' => '/external-custom-link'
		'children' => []
	],
	// etc...
]
@wkhayrattee
Copy link

this is worthy of being "LEGEND!". You rock for this recursive solution!

@Chukatuk
Copy link

Chukatuk commented Jun 8, 2016

Broke down my menu to get custom fields and couldn't get it back for 3 levels of sub menu or more...xD
You saved my a** =))
I tweaked it a bit so it should support taxonomies and generally everything else...
... and can get to custom fields a lot easier

@carlosalbertocruz
Copy link

How now list the menus?

@kaoskeya
Copy link

Hey, this did not seem to work for taxonomies, post archive, etc. I made https://gist.github.com/kaoskeya/5f31c235c29b307cbda4ca69bdd29fc9

@elishaukpong
Copy link

You saved me after three days of struggling to get this done, thanks

@ali-garajian
Copy link

The code is awesome and helped me a lot. There's only one bug in there that I nearly spent half a day to fix!
The problem was that the API didn't return all menu items, and the reason was that "posts_per_page => -1" wasn't mentioned as an argument!

@vwasteels
Copy link
Author

@ali-garajian Thanks for noticing this , it's now updated !

@DKudleichuk
Copy link

DKudleichuk commented Mar 8, 2021

taxonomy support:

`
switch( $type):

			case 'post_type':
				$postId = get_post_meta( $tmpItem->ID, '_menu_item_object_id', true);
				$post = get_post( $postId);
				$item->name = $post->post_title;
				$item->url = get_permalink( $post->ID );
			break;

			case 'taxonomy':

				$tax_id = get_post_meta( $tmpItem->ID, '_menu_item_object_id', true );
				$tax_name = get_post_meta( $tmpItem->ID, '_menu_item_object', true );
				$term = get_term_by( 'ID', $tax_id, $tax_name );
				$item->name = $term->name;
				$item->url = get_term_link( $term, $tax_name );
				
			break;

			case 'custom':
				$item->name = $tmpItem->post_title;
				$item->url = get_post_meta( $tmpItem->ID, '_menu_item_url', true);
				
			// note : this has to be completed with every '_menu_item_type' (could also come from plugin)

		endswitch;`

@marco910
Copy link

marco910 commented Jun 7, 2022

@vwasteels Awesome! Thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment