
A few posts ago I asked a question about the modularity of WordPress themes. In that post I promised to reveal my menu class, so here it is.
I recently started developing a theme for “the platform that shall not be named”. As I was doing it I realised that I had the opportunity to develop a class that worked the way I wish the WordPress menu functionality worked, and to make it cross platform so it could be easily shared.
What I came up with is a class (two actually) that can be used in themes to create any number of menus, using any content (not just pages), to any depth of nesting, with a great deal of flexibility.
This class not only outputs flat menus, it can also output dropdown menus in suckerfish style (you will need to supply the CSS and / or Javascript), nested menus that display the content when an appropriate parent or child is selected, separate menus for each level of nesting (i.e. if you want the submenu to appear in the sidebar with the main menu at the top) and breadcrumbs.
The basic function of the menu is very simple. Here is an example:
The following steps are all in the functions.php file unless I specify otherwise
Include the necessary classes, create a new menu, and set the menu id.
[php]
require_once('classes/onion_menu.php');
require_once('classes/onion_menu_item.php');
$onion_menu = new onion_menu();
$onion_menu->id = 'my_menu';
The menu ID will be used in the HTML that is output for styling purposes.
Create a function that runs just before the template is added to add in all the necessary pages:
[php]
add_action('template_redirect','load_menu');
function load_menu(){
global $post, $onion_menu;
//the next steps until I say otherwise
//are code to be included in this function
}
This is for a main menu, so we will want to include the home page. This will serve as a good example of the basic workings of the menu:
[php]
//add the home page in
$home_params = array();
$home_params['id'] = array("id"=>1,"type"=>'home');
$home_params['name'] = 'Home';
$home_params['description'] = 'The home page';
$home_params['url'] = get_bloginfo('url');
$onion_menu->add_item($home_params);
//set the current item to the home page, unless other wise specified
$onion_menu->set_current_item(array("id"=>1,"type"=>'home'));
The array $home_params is filled with the information needed to create a new menu item:
The ID of each item is an associative array that contains an ID number and the type of item. In this case the type is ‘home’. Home, is a special type so the menu recognises the home page and always displays it, no matter what item is selected.
The benefit of using both id and type is that items can be added that have the same ID within WordPress, so for example a category and a page might both have an id of 7, but the types will make sure they do not get confused.
The rest of the content in the array should be self evident;although there are more parameters that can be used that I will come back to later on.
Then the add method is called with the param_array as a paramater. This creates a new item within the menu.
Finally, we tell the menu that that currently selected item is the home page by passing the same id array as we gave in the parameter id.
Having a home page isn’t enough, so we need to add in the WordPress pages:
[php]
//add all the pages
$pages = get_pages();
foreach($pages as $page){
//setup the paramaters for this page
$page_params = array();
$page_params['id'] = array("id"=>$page->ID,"type"=>'page');
$page_params['name'] = $page->post_title;
$page_params['description'] = $page->post_excerpt;
$page_params['url'] = get_permalink($page->ID);
$page_params['position'] = $page->menu_order;
if (!empty($page->post_parent)){
$page_params['childof'] = array("id"=>$page->post_parent,"type"=>'page');
}
//actually create the page in the menu
$onion_menu->add_item($page_params);
}
if (is_page()){
$onion_menu->set_current_item(array("id"=>$post->ID,"type"=>'page'));
}
This is mostly a repeat of the code we used to add the home page in, repeated for each page in the WordPress list of pages, but there are a few differences here.
Firstly the id. The type is now ‘page’ and the id is the internal WordPress id.
Secondly we are also using the position parameter. This will be used to sort the menu items before they are displayed and we are giving it the menu_order field that pages can be given in WordPress.
Finally, if the page has a parent set, then the ‘childof’ parameter is being set. This uses the same format as the id, the id number and the type. It will mark this item as a child page and control how and when it is shown.
The last line checks is_page and if so tells the menu which page we are viewing so it can show the appropriate menu.
So far all we have really done is to replicate the content already in the WordPress menu item, so let’s add something slightly different. This is a long piece of code so bear with me:
[php]
//setup a list of categories that matter to the menu
$relevant_categories = array(11);
//check to see if we are viewing an actual post,
if (is_single()){
//check the categories the post is in
foreach($relevant_categories as $rc){
//check if the post is in this category
if ( in_category($rc)){
$in_category = $rc;
break;
}
}
if ( isset($in_category) ) {
//add the category, but do not show in the menu
$cat = get_category($in_category);
$cat_params = array();
$cat_params['id'] = array("id"=>$cat->cat_ID,"type"=>'category');
$cat_params['name'] = $cat->cat_name;
$cat_params['description'] = 'View posts in ' . $cat->cat_name;
$cat_params['url'] = get_category_link($cat->cat_ID);
$cat_params['childof'] = array("id"=>1,"type"=>'home');
$cat_params['showinmenu'] = false;
//add it into the menu
$onion_menu->add_item($cat_params);
//add the post as a child of the category
$post_params = array();
$post_params['id'] = array("id"=>$post->ID,"type"=>'post');
$post_params['name'] = $post->post_title;
$post_params['description'] = $post->post_excerpt;
$post_params['url'] = get_permalink($post->ID);
$post_params['childof'] = array("id"=>$cat->cat_ID,"type"=>'category');
$post_params['showinmenu'] = false;
//add it into the menu
$onion_menu->add_item($post_params);
//set the current item to this post
$onion_menu->set_current_item(array("id"=>$post->ID,"type"=>'post'));
} else {
//add the post as a sub-item of home
$post_params = array();
$post_params['id'] = array("id"=>$post->ID,"type"=>'post');
$post_params['name'] = $post->post_title;
$post_params['description'] = $post->post_excerpt;
$post_params['url'] = get_permalink($post->ID);
$post_params['childof'] = array("id"=>1,"type"=>'home');
$post_params['showinmenu'] = false;
$onion_menu->add_item($post_params);
//set the current item to this post
$onion_menu->set_current_item(array("id"=>$post->ID,"type"=>'post'));
}
}
So what does this do?
The first line is an array of the categories that we thing are important. They would be perhaps the five key topics of a blog, assuming you use categories that way.
Next we check to see if the page being displayed is actually a single post page. If not we won’t do anything.
The next thing we do is loop through the categories and check to see if the post is assigned to one of these categories. If it is we record the category ID and move on.
We now enter two branches. If the post was in one of our categories we do one thing, if not we do another. So if the post was in one of our categories:
We get all the details of the category and we create a menu item for that category. We assign it to be a child of the home page and we use the new parameter of showinmenu = false. This might seem odd, but it will make sense later on.
Next we get all the details of the post, and we add that to the menu as a child of the category. Again we use showinmenu = false.
We tell the menu that the post is the currently selected item.
If the post was not in one of our categories we add the post only, as a child of the home page, still using showinmenu = false, and setting the post as the selected item.
We now have the menu setup so we need to move on to decide how to display it. For this we use two functions. The first goes in sidebar.php:
[php]
global $onion_menu;
echo $onion_menu->get_menu(1,true,false,array('onion-menu'));
?>
Because of the way sidebar.php is included you need to use global to access $onion_menu. There may be a way around this, perhaps someone can say so in the comments if they know, but for the time being this will work.
The important part of this is the get_menu function. It takes the following options:
For the following explanations assume that you have three pages: ‘About’, ‘Stuff’, and ‘Things’. The ‘About’ page has two child pages ‘Work’ and ‘Play’.
Nest level dictates the first level of content that is displayed. So setting it to 2 will not show any pages that do not have children, but it will show the first set of children. (It will not show About, but it will show Work and Play). This means you can have the main menu in the header if you want, displaying About, Stuff, and Things and then call this function a second time in the sidebar of the page.php template and show the pages that are the children of the currently selected page. So when you click About in the main menu it will open the About page and the sidebar menu will contain Work and Play.
If Inline is set to false it will only show the items on the selected nest level. If it is set to true it will show any children, and children of children etc, that are descended from the current item. For example, if nest level is set to 1 and Inline is set to true, then when About is the selected page this will add a submenu containing Work and Play beneath About. if Inline is set to false it will not show the sub-menu.
If Dropdown is set to true it will always include every sub-menu of every item at every nest level. This lets you use CSS or Javscript to hide items until the parent item is hovered over.
Finally, classes take an array of classes that will be added to the ul tag for this particular instance of the menu, allowing for better control over CSS styling.
These explanations were not easy to write, and may be even harder to understand. The best idea is just to have a play with them.
The second function is this. It should be added in an appropriate place but I have assumed that it will be be added in the header.php file
[php]
You are here: get_breadcrumbs(); ?>
This function will create breadcrumbs using the menu data. This is reason for adding content that was not meant to be shown in the menu. So if you visited a post in the category ‘Things’ and the post title was ‘Stuff I done’ then the menu will appear exactly as if the home page was being viewed, but the breadcrumbs would show:
You are here: Home > Things > Stuff I done
Each of those three locations would be linked to the home page, category page, or post page respectively.
This is something I am still developing. There are other things in the menu class that will be of interest to theme developers but which do not work yet. I will save those for another day. It also means that it is not perfected. I certainly welcome any input.
If you use it in a theme I would also love to know so I can give out some props.
The theme I am developing for “the platform that shall not be named” is available on Google Code, and this forms part of it. The menu classes are in:
http://code.google.com/p/onion-theme/source/browse/#svn/trunk/themes/onion-theme/classes.
(__)
`
This looks highly useful. Now I just need to figure out how to use it and what it does !?!?
It looks like something useful for my Multi-level Navigation plugin.
(__)
`
[...] Menu Master Class [...]