August 22, 2016 · wordpress menu custom-walker wp_setup_nav_menu_item

Handling menu parent items in Wordpress

Menus in Wordpress are notorious for their API. While it comes with some mighty features it is not the best designed API in the Wordpress stack.
At least, everybody who had to implement their own walker class will know that.

Today I had quite a special situation in a complex hierarchical menu. It is based on a custom walker function that does the heavy lifting. But I think the problem I ran into today could hit you even without a custom walker.

What's the problem exactly? You have a child menu item and you want to get the url where its parent is pointing at. Simple enough.

So let's say we have a menu item as WP_POST object. It will look something link this (var_dump proudly borrowed from https://css-tricks.com/the-wordpress-nav-walker-class-a-guided-var_dump/ ):

object(WP_Post)#358 (40) {  
  ["ID"]                    => int(68)
  ["post_author"]           => string(1) "1"
  ["post_date"]             => string(19) "2015-10-07 01:05:49"
  ["post_date_gmt"]         => string(19) "2015-10-07 01:05:49"
  ["post_content"]          => string(1) " "
  ["post_title"]            => string(0) ""
  ["post_excerpt"]          => string(0) ""
  ["post_status"]           => string(7) "publish"
  ["comment_status"]        => string(6) "closed"
  ["ping_status"]           => string(6) "closed"
  ["post_password"]         => string(0) ""
  ["post_name"]             => string(2) "68"
  ["to_ping"]               => string(0) ""
  ["pinged"]                => string(0) ""
  ["post_modified"]         => string(19) "2015-10-07 01:05:49"
  ["post_modified_gmt"]     => string(19) "2015-10-07 01:05:49"
  ["post_content_filtered"] => string(0) ""
  ["post_parent"]           => int(0)
  ["guid"]                  => string(33) "http://localhost/wp/csstnav/?p=68"
  ["menu_order"]            => int(1)
  ["post_type"]             => string(13) "nav_menu_item" 
  ["post_mime_type"]        => string(0) ""
  ["comment_count"]         => string(1) "0" 
  ["filter"]                => string(3) "raw"
  ["db_id"]                 => int(68)
  ["menu_item_parent"]      => string(1) "0"
  ["object_id"]             => string(2) "50"
  ["object"]                => string(4) "page"
  ["type"]                  => string(9) "post_type"
  ["type_label"]            => string(4) "Page"
  ["url"]                   => string(28) "http://localhost/wp/csstnav/"
  ["title"]                 => string(10) "Front Page"
  ["target"]                => string(0) ""
  ["attr_title"]            => string(0) ""
  ["description"]           => string(0) ""
  ["classes"]               => array(8) {
    [0]=> string(0) ""
    [1]=> string(9) "menu-item" 
    [2]=> string(24) "menu-item-type-post_type" 
    [3]=> string(21) "menu-item-object-page" 
    [4]=> string(17) "current-menu-item" 
    [5]=> string(9) "page_item"
    [6]=> string(12) "page-item-50"
    [7]=> string(17) "current_page_item"
  }
  ["xfn"]                   => string(0) "" 
  ["current"]               => bool(true)
  ["current_item_ancestor"] => bool(false)
  ["current_item_parent"]   => bool(false)
}

In my case the interesting line was menu_item_parent. It holds the ID to the menu item one level further up. Please not, that the menu ID is not the same as the page ID. Each menu item needs to be viewed as a WP_POST object with a menu custom post type.
The referenced page id can be found under the object_id member of the object.
The post type of the referenced page can be called via object.

In order to get the details of the parent item, we can perform the following operation:

$parent = get_post($item->menu_item_parent);

which will yield something like:

object(WP_Post)#12190 (24) {  
  ["ID"]=>  int(168741)
  ["post_author"]=>  string(1) "1"
  ["post_date"]=>  string(19) "2016-08-22 14:15:05"
  ["post_date_gmt"]=>  string(19) "2016-08-22 12:15:05"
  ["post_content"]=>  string(1) " "
  ["post_title"]=>  string(0) ""
  ["post_excerpt"]=>  string(0) ""
  ["post_status"]=>  string(7) "publish"
  ["comment_status"]=>  string(6) "closed"
  ["ping_status"]=>  string(6) "closed"
  ["post_password"]=>  string(0) ""
  ["post_name"]=>  string(6) "168741"
  ["to_ping"]=>  string(0) ""
  ["pinged"]=>  string(0) ""
  ["post_modified"]=>  string(19) "2016-08-22 14:42:03"
  ["post_modified_gmt"]=>  string(19) "2016-08-22 12:42:03"
  ["post_content_filtered"]=>  string(0) ""
  ["post_parent"]=>  int(970)
  ["guid"]=>  string(47) "some guid"
  ["menu_order"]=>  int(39)
  ["post_type"]=>  string(13) "nav_menu_item"
  ["post_mime_type"]=>  string(0) ""
  ["comment_count"]=>  string(1) "0"
  ["filter"]=>  string(3) "raw"
}

This looks nothing like the first menu item. Instead of 40 properties, we only have 24 and all the important ones are missing, too.

The solution? There is a special Wordpress function that appends the missing menu specific information to the returned WP_POST object:

$parent = wp_setup_nav_menu_item($parent);

Doesn't it roll right of you tongue? Calling that function yields an object with the missing url, object and other properties that are helpful when dealing with menu items.

You can read more about wp_setup_nav_menu_item in the Wordpress reference.

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket
Comments powered by Disqus