5

I need creating a series of menu items dynamically based on values from the database.

Here is a example of my hook_menu() function; in this snippet, I removed the DB queries and just added an array to prove the concept:

function MYMODULE_menu() {
  $years = array(2011, 2010, 2009);

  foreach ($years as $year) {
    $items['events/' . $year] = array(
      'title' => $year . ' Events Archive',
      'page callback' => 'page_archive',
      'page arguments' => array(1),             
      'access callback' => TRUE,
      'type' => MENU_NORMAL_ITEM,
      'menu_name' => 'main-menu',
    );  
  }

  return $items;
}

This works and I get three menu items in the Main Menu titled 2011 Events Archive, 2010 Events Archive and 2009 Events Archive.

The problem I am having is the position of the dynamically created menu items in the Main Menu tree; they appear at the top level, but I want they appear under the parent Events menu item.

Currently, my menu tree looks something similar to the following.

  • Home
  • Events
  • About Us
  • Contact Us
  • 2011 Events Archive
  • 2010 Events Archive 2009 Events Archive

It should look like the following.

  • Home
  • Events
    • 2011 Events Archive
    • 2010 Events Archive
    • 2009 Events Archive
  • About Us
  • Contact Us

I know you can move the menu items using the Menu editor UI when logged in, but as these will be automatically created by the system; they need to appear automatically under Events without human intervention.

How can this be done? Is there an easier way to do this?

3
  • 1
    What do you use for the "events" menu item?
    – apaderno
    May 13, 2011 at 13:50
  • Not sure what you mean? It's just a standard node with a list of current events on it. There needs to be sub menu items for the archived events.
    – Camsoft
    May 16, 2011 at 8:31
  • Usually the parent of a menu item defined in hook_menu() is another menu item defined from hook_menu() in any module. If "Events" is a menu item that links to a node, then there is a problem.
    – apaderno
    May 16, 2011 at 13:09

3 Answers 3

6

Since the Events menu item links to a node, for which events is a path alias, what you are trying to achieve should work if you define the menu items using the following code (I use "11" as node ID).

function MYMODULE_menu() {
  $years = array(2011, 2010, 2009);

  foreach ($years as $year) {
    $items['node/11/' . $year] = array(
      'title' => $year . ' Events Archive',
      'page callback' => 'page_archive',
      'page arguments' => array(1),             
      'access callback' => TRUE,
      'type' => MENU_NORMAL_ITEM,
      'menu_name' => 'main-menu',
    );  
  }

  return $items;
}

This is necessary because, if events is a path alias for node/11, Drupal doesn't expand events/2011 to node/11/2011. In the same way, it will not expand events/edit to node/11/edit, even thought the Node module defines nodes/%node/edit as one of its menu items.

2
  • can you elaborate more on "This happens because path aliases are not resolved, for paths used inside hook_menu()"?
    – Randell
    Nov 18, 2013 at 7:22
  • If events is a path alias for node/1, events/edit will be not resolved to node/1/edit. When Drupal looks for a path alias, it doesn't look for a partial match. So, with example.com/events/edit, Drupal will check if events/edit is a path alias; if it is not a path alias, then it will look for any module that uses events/edit in hook_menu()
    – apaderno
    Nov 18, 2013 at 7:26
2
<?php
/**
 * GOAL AND STRATEGY:
 *
 * node/6 is a top level menu item in a custom menu called 'menu-main-menu'. I'm
 * creating a child menu item using hook_menu with the path of 'member/pick'. I
 * want this menu item to be rendered as a child of node/6 in the main menu, and
 * I want all of this to happen programatically.
 * 
 * Here's how...
 * 
 * 1. Create the child item using hook_menu
 * 2. In hook_menu_link_alter, lookup the mlid of node/6 and apply it as the
 * plid of the child item
 * 
 */

/**
 * Implementation of hook_menu()
 */
function hook_menu() {
  $items = array();
  $items['member/pick'] = array(
    'title' => 'Member Pick',
    'menu_name' => 'menu-main-menu',
    'page callback' => 'member_pick_page',
    'access callback' => TRUE,
    'type' => MENU_NORMAL_ITEM,
  );    
  return $items;
}

/**
 * Implementation of hook_menu_link_alter
 */
function hook_menu_link_alter(&$item) {
  if ($item['page callback'] == 'member_pick_page') {
    $parent_normal_path = 'node/6';
    $plid = db_result(db_query("SELECT mlid FROM {menu_links} WHERE link_path = '%s'", $parent_normal_path));
    $item['plid'] = $plid;
  }
}
0

Here's how I do it... First, create a menu item that will be representing the group:

$items['admin/content/example'] = array(
  'title' => 'Example menu group',
  'page callback' => 'example_page_2011',
  'access arguments' => array('administer nodes'),
);

Then add your menu items below this group:

foreach ($years as $year) {
  $items['admin/content/example/' . $year] = array(
    'title' => $year,
    'page callback' => 'example_page_' . $year,
    'access arguments' => array('administer nodes'),
    'type' => MENU_LOCAL_TASK,
  );
}

Event though it works (tested on 7.22), I still don't like that redundancy in page callback. If only there was a type called MENU_GROUP... Any better solution is appreciated.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.