Creating a Menu Position Rule Programatically

The Menu Position module provides a powerful way for Drupal to create "virtual" menu items, and provides a graphical user interface to do so. However, as a developer, sometimes you want to create those rules in code. Here's how.

I did some Googling on this for a while, and came up short. The best answer I found was stalled in the Menu Position 7.x-2.x issue queue, and stymied by the fact that mlid values are not stable from site-to-site.

Overview

Menu Position Rule by Content Type

Let's say you want to create a menu position rule, "All Event Nodes", to create a virtual menu item for every Event node under the top-level "Events" item in the main menu.

First, you want to use module_load_include() to load the menu_position.admin.inc include file, so that you have access to the menu_position_add_rule() function:

// load the menu_position.admin.inc include file
module_load_include('inc', 'menu_position', 'menu_position.admin');

Next, you want to get the mlid of the parent menu link item for your Menu Position rule:

// Grab the mlid of the parent menu link item
$result = db_select('menu_links', 'm')
  ->fields('m', array('mlid'))
  ->condition('menu_name', 'main-menu') // "main-menu" is the machine name of the Main Menu.
  ->condition('depth', 1) // assuming a depth of 1 in our Main Menu
  ->condition('link_title', 'Events') // assuming there are no other menu items with a depth of 1 in our main menu that are also named "Events"
  ->execute()
  ->fetchAssoc();

$plid = $result['mlid'];

Finally, create an array with the details of your Menu Position rule, and save it:

// create the array to populate the rule
$rule = array(
  'admin_title' => 'All Event Nodes',
  'conditions' => array(
    'content_type' => array(
      'content_type' => array(
        'event' => 'event', // "event" is the machine name of the content type
      ),  
    ),  
  ),  
  'menu_name' => 'main-menu',
  'plid' => $plid,  // "Events" item in main menu.
);

// By calling menu_position_add_rule() here, we're assuming that this exact rule does not exist
menu_position_add_rule($rule);

Other Types of Conditions

Menu Position offers additional conditions beyond node type. Here are a few examples.

Path Condition

You would build your $rule array a little differently if you wanted to use a path condition:

$rule = array(
  'admin_title' => 'Events by Path',
  'conditions' => array(
    'pages' => array(
      'pages' => 'events/*', // activate the Menu Position rule on every path under "events/"
    ),  
  ),  
  'menu_name' => 'main-menu',
  'plid' => $plid,  // "Events" item in main menu.
);

Taxonomy Condition

You would want to do an additional lookup with taxonomy_vocabulary_machine_name_load() to look up a vocabulary's vid by name:

$vocabulary = taxonomy_vocabulary_machine_name_load('event_categories');
$vid = $vocabulary->vid;
$rule = array(
  'admin_title' => 'Events by Taxonomy',
  'conditions' => array(
    'taxonomy' => array(
      'vid' => $vid, // the vid of the "Event Categories" vocabulary (machine name "event_categories")
      'tid' => array(),
    ),
  ),
  'menu_name' => 'main-menu',
  'plid' => $plid,  // "Events" item in main menu.
);

Other Conditions Not Listed Above

Taxonomy, path, and content type were the only conditions I had a need for at the time I discovered this trick. However, you can decode other conditions by peeking at the conditions field in the {menu_position_rules} database table. The conditions are stored as a serialized array; you can use the PHP unserialize() function to get the array structure:

$ drush sqlq --extra="-t" 'select * from menu_position_rules limit 1'
+-----+-------------+---------+----------------------------------------------------------------------------------+-----------+------+------+--------+
| rid | admin_title | enabled | conditions                                                                       | menu_name | plid | mlid | weight |
+-----+-------------+---------+----------------------------------------------------------------------------------+-----------+------+------+--------+
|   1 | News        |       1 | a:1:{s:12:"content_type";a:1:{s:12:"content_type";a:1:{s:4:"news";s:4:"news";}}} | main-menu | 1173 | 1289 |      0 |
+-----+-------------+---------+----------------------------------------------------------------------------------+-----------+------+------+--------+

Using dpm() or print_r() shows you the array structure:

print_r(unserialize('a:1:{s:12:"content_type";a:1:{s:12:"content_type";a:1:{s:4:"news";s:4:"news";}}}'));
Array
(
    [content_type] => Array
        (
            [content_type] => Array
                (
                    [news] => news
                )
        )
)
Categories: 

Add new comment

By submitting this form, you accept the Mollom privacy policy.