15

A newbie question guys.

Any thoughts how can I create a menu programmatically? Says I have 3 different menus I want to create. The first menu will be placed in the top left of the header. The second menu will be placed below the first menu. The third menu will be the main navigation.

Could these the menus be under the same group? Would it be a problem in styling?

Thanks

25

If you are trying to do this in an update script, this should work:

$menus = array(
  array(
    'menu_name' => 'menu_test_one',
    'title' => 'My Menu One',
    'description' => 'Lorem Ipsum',
  ),
  array(
    'menu_name' => 'menu_test_two',
    'title' => 'My Menu Two',
    'description' => 'Lorem Ipsum',
  ),
  array(
    'menu_name' => 'menu_test_three',
    'title' => 'My Menu Three',
    'description' => 'Lorem Ipsum',
  ),
);

$links = array(
  array(
    array(
      'link_title' => 'Link1',
      'link_path' => 'http://yourdomain.com/link1',
      'menu_name' => 'menu_test_one',
      'weight' => 0,
      'expanded' => 0,
    ),
    array(
      'link_title' => 'Link2',
      'link_path' => 'http://yourdomain.com/link2',
      'menu_name' => 'menu_test_one',
      'weight' => 1,
      'expanded' => 0,
    ),
  ),
  array(
    array(
      'link_title' => 'Link3',
      'link_path' => 'http://yourdomain.com/link3',
      'menu_name' => 'menu_test_two',
      'weight' => 0,
      'expanded' => 0,
    ),
    array(
      'link_title' => 'Link4',
      'link_path' => 'http://yourdomain.com/link4',
      'menu_name' => 'menu_test_two',
      'weight' => 1,
      'expanded' => 0,
    ),
  ),
  array(
    array(
      'link_title' => 'Link5',
      'link_path' => 'http://yourdomain.com/link5',
      'menu_name' => 'menu_test_three',
      'weight' => 0,
      'expanded' => 0,
    ),
    array(
      'link_title' => 'Link6',
      'link_path' => 'http://yourdomain.com/link6',
      'menu_name' => 'menu_test_three',
      'weight' => 1,
      'expanded' => 0,
    ),
  ),
);

// Save menu group into menu_custom table
foreach ($menus as $menu) {
  // Look the table first if the data does exist
  $exists = db_query("SELECT title FROM {menu_custom} WHERE menu_name=:menu_name", array(':menu_name' => $menu['menu_name']))->fetchField();
  // Save the record if the data does not exist
  if (!$exists) {
    menu_save($menu);
  }
}

$item = ''; 
foreach ($links as $layer1) {
  foreach ($layer1 as $link) {
    // Build an array of menu link 
    $item = array(
      'link_path' => $link['link_path'],
      'link_title' => $link['link_title'],
      'menu_name' => $link['menu_name'],
      'weight' => $link['weight'],
      'expanded' => $link['expanded'],
    );
    // Look the table first if the data does exist
    $exists = db_query("SELECT mlid from {menu_links} WHERE link_title=:link_title AND link_path=:link_path", array(':link_title' =>  $link['link_title'], ':link_path' => $link['link_path']))->fetchField();
    // Save the record if the data does not exist
    if (!$exists) {  
      menu_link_save($item);
    }
  }
}

Comments are welcome if my approach is wrong. Thanks.

  • This is exactly what I want. Let me try your code and will give feedback later. Thanks for sharing – user8012 Sep 6 '12 at 7:53
  • 5
    Instead of running the $exists db query a more future proof (but maybe slightly slower) method would be to just use if (!menu_load($menu['menu_name'])) { menu_save($menu); } – Bala Clark Oct 8 '12 at 10:13
  • Cool, nice one! – Tyler Durden Apr 10 '14 at 17:44
  • My suggestion would be to use menu_get_menus() instead of a query. Then you can save the menus to a var and simply do an if (!array_key_exists($menu, $menus)) { - also adding the single param FALSE to menu_get_menus() returns only custom menus. – Christian Jul 2 '14 at 8:27
  • 1
    Also when you check if the menu link exist: // Look the table first if the data does exist $exists = ... You need also to check for menu_name as the same link combination can be in different menus. – hugronaphor Feb 3 '15 at 11:53
4

Here's a way to easily populate a menu from an array :

<?php
function populate_menu($links, $menu_name, $plid = 0) {
  foreach ($links as $link) {
    $ls = array(
      'menu_name' => $menu_name,
      'link_title' => $link['link_title'],
      'link_path' => $link['link_path'],
      'plid' => $plid,
    );
    $newpid = menu_link_save($ls);
    if (!empty($link['childs'])) {
      populate_menu($link['childs'], $menu_name, $newpid);
    }
  }
}


$items = array(
  array(
    'link_title' => 'Menu1',
    'link_path' => '<front>',
    'childs' => array(
      array(
        'link_title' => 'Sub Item 1',
        'link_path' => '<front>',
        'childs' => array(
          array(
            'link_title' => 'Link item 1',
            'link_path' => '<front>',
          ),
          array(
            'link_title' => 'Link item 2',
            'link_path' => '<front>',
          ),
        ),
      ),
      array(
        'link_title' => 'Sub Item 2',
        'link_path' => '<front>',
        'childs' => array(
          array(
            'link_title' => 'Link item',
            'link_path' => '<front>',
          ),
        ),
      ),
    ),
  ),
);
populate_menu($items, 'main-menu');
2

hook_menu() is all you need to implement in you custom module. For creation of custom module, refer to this documentation.

//Define the menus in the function which goes in your MYMODULE.module file  
function MYMODULE_menu() {
  //the menu which will point to http://yoursite/first-menu
  $items['first-menu'] = array(
    'title' => 'First menu',  // will appear as the name of the link
    // Page callback, etc. need to be added here.
  );

  //the menu which will point to http://yoursite/second-menu
  $items['second-menu'] = array(
    'title' => 'Second menu',  // will appear as the name of the link
    // Page callback, etc. need to be added here.
  );

  //the menu which will point to http://yoursite/third-menu
  $items['third-menu'] = array(
    'title' => 'third menu',  // will appear as the name of the link
    // Page callback, etc. need to be added here.
  );

  return $items;
}

You could print the menu in any region by adding the following code to the page.tpl.php file of you theme.

//add this line in <div id="header"></div> to print it in header.
<?php
$menu1 = menu_navigation_links('first-menu');
print theme('links__first_menu', array('links' => $menu1));

//print second menu just below first menu
$menu2 = menu_navigation_links('second-menu');
print theme('links__second_menu', array('links' => $menu1));
?>

You don't need to print third-menu because by default it will appear at the navigation menu.


NOTE: This is not best practice at all for creating a navigation menu and adding it to a page. hook_menu() is for creating page callbacks and not for creating navigation menus. Please read THIS, which explains the differences. I answered this when I had started to learn Drupal and I don't recommend this answer anymore.

  • 1
    This is not best practice at all for creating a navigation menu and adding it to a page. hook_menu() is for creating page callbacks and not for creating navigation menus. Please read the following which explains the differences. api.drupal.org/api/drupal/includes!menu.inc/group/menu/7 – Christian Jul 2 '14 at 8:19
  • 1
    @Christian I had just started to learning Drupal while answering this; and the way I prefered to learn Drupal was by answering questions here by actually trying things out. At the current level of knowledge I have, I would definitely vote this type of answer down too; but I am actually kind of proud of the way I thought in the beginning :) I appreciate the way you explained and the link provided. – AjitS Jul 2 '14 at 8:24
  • Hey AjitS, all good - i dont vote answers down often. I would simply add an update before the answer that says that you no longer suggest this approach. Thanks for the reply. – Christian Jul 2 '14 at 8:31
  • have removed my downvote. – Christian Jul 3 '14 at 6:26
0

You can also try the Menu Import module. It's very cool and easy for menu deployment. You can create menu by hands on your website and use generated JSON. Also you can create nodes for non-existing pages.

Here is example of export script:

$menu_name = 'menu-footer-secondary-menu';

// Create menu if doesn't exist.
if (!menu_load($menu_name)) {
  $menu = array(
    'menu_name' => $menu_name,
    'title' => t('Footer secondary menu'),
    'description' => '',
  );
  menu_save($menu);
}

// Import menu links from JSON.
$menu_string = 'Impressum {"url":"node\/1","options":{"attributes":[]}}
Datenschutzbestimmungen {"url":"node\/2","options":{"attributes":[]}}
Nutzungsbedingungen {"url":"node\/3","options":{"attributes":[]}}
';

$options = array(
  //'link_to_content' => TRUE, // Look for existing nodes and link to them.
  'create_content' => TRUE, // Create new content (also if link_to_content not set).
  'remove_menu_items' => TRUE, // Removes current menu items.
  'node_type' => 'page',
  'node_body' => 'Page is in development...',
  'node_author' => 1,
  'node_status' => 1,
);

menu_import_string($menu_string, $menu_name, $options);

You can run this script with hook_update_N() or Update script processor

0

For creating a menu block, in your .install file implementing hook_enable() type

function mymodule_enable() {
  $t = get_t();
  if (module_exists('menu')) {
    menu_save(array(
      'menu_name' => 'my-new-menu',
      'title' => $t('My New Menu'),
      'description' => $t('My Menu description'),
    ));
  }
 }

In the same .install file, implementing hook_uninstall().

function mymodule_uninstall() {
  if (module_exists('menu')) {
   if ($my_menu = menu_load('my-new-menu')) {
    menu_delete($my_menu);
  }
 }
}

Next in your .module file, while implementing hook_menu().

function mymodule_menu() {
  return array(
   'mypage' => array(
   'title' => 'My Page',        
   'description' => 'My Page description',    
   'page callback' => 'my_page_callback_func',
   'page arguments' => array(arg(0)),
   'access arguments' => array('access content'),
   'menu_name' => 'my-new-menu',
   'file' => 'my.new.page.inc',
   'file path' => drupal_get_path('module', 'mymodule') . '/includes',
  ),
 );     
}

The .inc file is withing includes folder placed within mymodule folder.

See devel module's devel.install and devel.module file for more information.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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