Need some help with your project? Contact me

Adding menu items with wildcards to Drupal 7 menus

In this article I am going to share with you a quick tip on how to get your custom menu item into an actual menu on the site.

A problem I encountered when declaring a menu item using hook_menu() is that if the menu item contains a wildcard, it just won't go into the menu. You can easily specify a menu_name and if you have no wildcards, it will show up nicely in the menu you specified. If however, you need some wildcards, it won't.

I needed one such menu item (imagine that) to be placed in one of my menus on the site. After trying all sorts of things and failing miserably at them, I decided to go at it through a preprocessor function and declare my menu item there. So let me show you how I did it and maybe you can tell me some better, more correct, ways to do it.

First of all, I needed to see where my menu was being printed on the page (it was the Secondary Menu in fact). If you look at the Bartik theme in the page.tpl.php file, you'll notice the following block:

<?php if ($secondary_menu): ?>
  <div id="secondary-menu" class="navigation">
    <?php print theme('links__system_secondary_menu', array(
      'links' => $secondary_menu,
      'attributes' => array(
        'id' => 'secondary-menu-links',
        'class' => array('links', 'inline', 'clearfix'),
      ),
      'heading' => array(
        'text' => t('Secondary menu'),
        'level' => 'h2',
        'class' => array('element-invisible'),
      ),
    )); ?>
  </div> <!-- /#secondary-menu -->
<?php endif; ?>

This is basically where the Secondary Menu gets printed onto the page. And since it's in the page.tpl.php file, we can go ahead and write a preprocessor function for the page in the theme's template.php file. I always have the Devel module installed locally so I can debug variables, so if you are following along, make sure you do too.

function bartik_preprocess_page(&$vars) {
  dpm($vars);
}

You'll notice I am hacking the Bartik theme but I'm only doing this for education purposes in order to see what's behind the hood. It will not go into any website like this. And make sure that if you do this, you don't redeclare the same preprocess function in the same theme or you'll get an error.

Having said that, with the function above, I am debugging the variables array that goes into the page.tpl.php file to be displayed. And what do you know, one of the keys of this array is secondary_menu. Great. Inside we have a number of elements keyed by the class name that gets applied to each menu item when it gets printed. If we look inside each element, we can gather what we need to add to this array in order for it to display another menu item: a href (path to which the menu will link) and a title (the link title that will be displayed).

If you are using wildcards, you need to make sure though that when building the href, you have all the necessary information in this preprocess function to actually build it. I needed to current user ID so that was available (plus I could always take the user object from the global scope).

So to add an example menu item, you'd do something like this:

$vars['secondary_menu']['my_menu'] = array(
  'href' => 'path/you/want/' . $id,
  'title' => 'My dynamic menu item',
);

Please note that the $id is fictional and you have to gather it from somewhere if you want to include it in the URL. I was just giving an example. But if you refresh the page now, the Secondary Menu should include your new menu item.

Bonus

As a bonus, I'll show you how to add attributes to the item you include in the menu. If we wanted to add a button class to the menu link, instead of the above, we'd do this:

$vars['secondary_menu']['my_menu'] = array(
  'href' => 'path/you/want/' . $id,
  'title' => 'My dynamic menu item',
  'attributes' => array('class' => array('button')),
);

Notice the extra attributes key we pass along. There you can include also other attributes that will get processed by the core drupal_attributes() function.

You can use this same technique to add classes to existing menu items as well, not necessarily new ones you are adding. A little theming lesson as well, similar to how we added classes to form elements in the article on theming forms.

Caveat

There is one problem with this approach, reason for which I kept trying to do it properly using hook_menu().

Since you are adding the menu item through the template preprocess function, it will only be displayed in the Secondary Menu of the page.tpl.php file that prints it out.

Let's say you have a block.tpl.php that for some reason needs to print out this menu as well. In this case, your new menu item won't show up. You'll have to include it in the preprocess function of the block.tpl.php as well (for e.g. bartik_preprocess_block()) the same way we did above.

Comments

I was pretty sure the article will be about great Menu Token module, but... :)

For those other cases, there's always https://drupal.org/project/menu_token ;)

Nice! Thanks!

Interesting - I'm surprised I've never run across this use case before.

This is a different (untested) idea, but if you happen to use Menu Block, it does provide hook_menu_block_tree_alter() to act on the menu tree after it has been loaded by menu_tree_all_data() or menu_tree_page_data(). This all happens in the module's menu_tree_build() function. So it may be possible to inject the link here, and it would affect all places where the menu block is rendered.

Drupal core offers a way to define default values to menus with wildcards, it is the %WILDCARD_to_arg functions. See https://api.drupal.org/api/drupal/modules%21system%21system.api.php/func... for more on that.

Add new comment

You can post comments in Markdown and basic HTML tags.
For code blocks, wrap your code within '~~~'. For example:
~~~
$var = 'my variable';
~~~