Sorting Content By a Menu Order
Posted on June 28, 2013
While working on a recent WordPess site, I ran into the requirement that the site needed a directory page to display the titles and excerpts from pages, in the same order that a sidebar menu lists links to the pages.
In this case, the client needed an employee sidebar menu, with links to each individual employee bio page, and also an ’employee directory’ page that lists all employee names and bio excerpts in the same order as the menu. The sidebar menu requires custom sorting that indicates the hierarchy of the employees, and the directory page should have this same sorting.
Below are screen shots of a test menu I set up on my web own site and a test page showing post data in the order of the menu.
At first I tried direct SQL queries which provided the desired functionality but where lengthy and required joining 5 tables. So I looked for a WordPress API based solution and arrived at the following coded solution.
Instead of using a query to get all of the posts in the order listed in the menu, I decided to instead use a coded solution to loop through the items in the menu and use WordPress functions to get the title and excerpt for each corresponding post.
First you will need to get the menu items of the desired menu. You can easily do this using the wp_get_nav_menu_items
function. Just pass the name of the menu you want the items for and this function will return an array with the objects for each menu item.
// pass the menu name to get the
// nav menu item objects for the items in the menu
$menu_items = wp_get_nav_menu_items('test menu');
Next you loop through each menu item and get the title and excerpt of the corresponding post. The $menu_item
object has a property called object_id
which is the post id of the corresponding post. You can pass this ID to the get_post
function to get the post data on each pass of the loop and then print out the post data.
// pass the menu name get the nav menu item objects
// for the items in the menu
$menu_items = wp_get_nav_menu_items('test menu');
global $post; // for use with setup_postdata
foreach ($menu_items as $menu_item ) {
// get the corresonding post id
$post_id = $menu_item->object_id;
// get the post data
$post = get_post($post_id);
// setup post data to use template tags
setup_postdata($post);
// print the required post data
print '
' .
get_the_title() . '
';
the_excerpt();
}
Filtering content
The code above assumes that you are pulling post data for each item in the menu. There are probably situations where you may need something different. For example, if you only wanted the posts corresponding to indented menu items, you could use an if/then statement to only get post data for the items that match a certain value in $menu_item->menu_item_parent
, and exclude all other posts. Another example might be that you only want to display posts of a certain post type like ‘pages’. In this second case your if/then statement can check the $menu_item->object
property for the desired post type.
Note that while this solution has less code compared to a customized SQL query, it requires multiple queries to MySQL instead of just one. There could be inefficiencies with pulling post data from large numbers of posts this way. So this approach is more appropriate if you have a small number of items in the menu, which is probably the most common situation anyway.