I finally installed Drupal 6 tonight and came across something that nearly knocked me out. The maximum depth of the menu tree is 9 items?

Why wouldn't this number be configurable? My site is far past this since I do use Drupal to create a large directory. Who predetermined 9 layers of depth and why?

I've only found one link that said that it seems to have been an arbitrary decision so that the menutree wouldn't be too wide on a page. Well, I solved that problem long ago by throwing the menu tree out and using submenutree in Drupal 5.

So now it seems this depth was arbitrarily predetermined and there is no way in the admin settings to over-ride it meaning I will likely have to hack core to get this functionality.

Can someone tell me if I'm seeing this correctly?

Comments

Alan D.’s picture

I just hacked my install to up this to 15. See http://drupal.org/node/274270


Alan Davison
www.caignwebs.com.au

Alan Davison
kevinquillen’s picture

"I've only found one link that said that it seems to have been an arbitrary decision so that the menutree wouldn't be too wide on a page."

This I don't understand. Drupal should not be restricting you for fear of breaking your layout- it should trust that you know what you are doing to make your layout capable of a large tree. If I want a menu with 50 branches- I should be able to have it no questions asked.

==============
delaware web design
delaware website design

===========
read my thoughts

Alan D.’s picture

The code would take much to port to a dynamic limit. Vote to push this into D7 before the code freeze...


Alan Davison
www.caignwebs.com.au

Alan Davison
Luca O’s picture

In my opinion there is no need of extremely deep menus, a taxonomy approach (that has no depth limit) should works almost in every case. Do you need more than 9 levels for what?

natuk’s picture

But it is true. Only 9 generations for any hierarchy.

Yes, taxonomy would cope with so many generations far better, but then what is the point of having the book module? We may as well, map everything in taxonomy, shallow and deep outlines.

In our project we are building a detailed classification system and 16 generations are not uncommon. Could we do it differently with the taxonomy module? Probably. But why having the option to use book if it is not working properly?

Alan D.’s picture

Deep nesting is a problem, but you can generate a deep menu using either technique.

While the native menu system is denormalized, it has better performance when generating the complete menu in one hit, as it effectively is a cache of the menu tree structure. The hack mentioned above works nicely, but it doesn't update the book module. [Ignore the crap about the taxonomy menu unless you require to modify performance issues with this. ;) ] I've attached an updated Drupal 6.4 patch below.

If you want page by page stepping into the data, use a taxonomy based menu. You'll only need a singular menu item and it is fairly easy to parse the parent tid and present the children. (Eg: action/tid is enough info for most tasks)

In the project that I'm using the core hack, the primary menu is generated from a regions taxonomy. It uses one menu router row and count(terms) rows for the menu items representing the regions that are 15 levels deep. This drives 8 or so views that are attached to the taxonomy. This is presented to the user as a javascript menu.

In the same project, I also use the second approach to generate dynamic menus that allow the users to parse the same taxonomy on a per view basis. Eg: "See all xxx of this region, or narrow your search to one of the following sub-regions".

Just updating my system to 6.4, so I've attached the patch below for 15 levels, BUT IT EXCLUDES REQUIRED BOOK PATCH (probably not hard, but I never use the book module): Should work fine on clean installs (untested), or by added the extra fields to the menu_links table directly, d10 ... d15, and applying the patches.

BACKUP FIRST AND TEST WELL

### Eclipse Workspace Patch 1.0
#P CVS Drupal 6.4
Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.255.2.17
diff -u -r1.255.2.17 menu.inc
--- includes/menu.inc	9 Jul 2008 15:23:50 -0000	1.255.2.17
+++ includes/menu.inc	14 Aug 2008 11:40:03 -0000
@@ -164,7 +164,7 @@
 /**
  * The maximum depth of a menu links tree - matches the number of p columns.
  */
-define('MENU_MAX_DEPTH', 9);
+define('MENU_MAX_DEPTH', 15);
 
 
 /**
@@ -821,7 +821,7 @@
         SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.*
         FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
         WHERE ml.menu_name = '%s'". $where ."
-        ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC", $args), $parents);
+        ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC, p10 ASC, p11 ASC, p12 ASC, p13 ASC, p14 ASC, p15 ASC", $args), $parents);
       $data['node_links'] = array();
       menu_tree_collect_node_links($data['tree'], $data['node_links']);
       // Cache the data, if it is not already in the cache.
@@ -885,12 +885,12 @@
             $args[] = '<front>';
             $placeholders .= ", '%s'";
           }
-          $parents = db_fetch_array(db_query("SELECT p1, p2, p3, p4, p5, p6, p7, p8 FROM {menu_links} WHERE menu_name = '%s' AND link_path IN (". $placeholders .")", $args));
+          $parents = db_fetch_array(db_query("SELECT p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 FROM {menu_links} WHERE menu_name = '%s' AND link_path IN (". $placeholders .")", $args));
 
           if (empty($parents)) {
             // If no link exists, we may be on a local task that's not in the links.
             // TODO: Handle the case like a local task on a specific node in the menu.
-            $parents = db_fetch_array(db_query("SELECT p1, p2, p3, p4, p5, p6, p7, p8 FROM {menu_links} WHERE menu_name = '%s' AND link_path = '%s'", $menu_name, $item['tab_root']));
+            $parents = db_fetch_array(db_query("SELECT p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14 FROM {menu_links} WHERE menu_name = '%s' AND link_path = '%s'", $menu_name, $item['tab_root']));
           }
           // We always want all the top-level links with plid == 0.
           $parents[] = '0';
@@ -928,7 +928,7 @@
           SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.*
           FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
           WHERE ml.menu_name = '%s' AND ml.plid IN (". $placeholders .")
-          ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC", $args), $parents);
+          ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC, p10 ASC, p11 ASC, p12 ASC, p13 ASC, p14 ASC, p15 ASC", $args), $parents);
         $data['node_links'] = array();
         menu_tree_collect_node_links($data['tree'], $data['node_links']);
         // Cache the data, if it is not already in the cache.
@@ -1150,7 +1150,7 @@
  */
 function drupal_help_arg($arg = array()) {
   // Note - the number of empty elements should be > MENU_MAX_PARTS.
-  return $arg + array('', '', '', '', '', '', '', '', '', '', '', '');
+  return $arg + array('', '', '', '', '', '', '', '', '', '', '', '','','','','','','');
 }
 
 /**
@@ -1956,11 +1956,13 @@
     router_path = '%s', hidden = %d, external = %d, has_children = %d,
     expanded = %d, weight = %d, depth = %d,
     p1 = %d, p2 = %d, p3 = %d, p4 = %d, p5 = %d, p6 = %d, p7 = %d, p8 = %d, p9 = %d,
+    p10 = %d, p11 = %d, p12 = %d, p13 = %d, p14 = %d, p15 = %d,
     module = '%s', link_title = '%s', options = '%s', customized = %d WHERE mlid = %d",
     $item['menu_name'], $item['plid'], $item['link_path'],
     $item['router_path'], $item['hidden'], $item['_external'], $item['has_children'],
     $item['expanded'], $item['weight'],  $item['depth'],
     $item['p1'], $item['p2'], $item['p3'], $item['p4'], $item['p5'], $item['p6'], $item['p7'], $item['p8'], $item['p9'],
+    $item['p10'], $item['p11'], $item['p12'], $item['p13'], $item['p14'], $item['p15'],
     $item['module'],  $item['link_title'], serialize($item['options']), $item['customized'], $item['mlid']);
   // Check the has_children status of the parent.
   _menu_update_parental_status($item);
Index: modules/menu/menu.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.admin.inc,v
retrieving revision 1.26.2.3
diff -u -r1.26.2.3 menu.admin.inc
--- modules/menu/menu.admin.inc	11 Feb 2008 15:12:53 -0000	1.26.2.3
+++ modules/menu/menu.admin.inc	14 Aug 2008 11:40:13 -0000
@@ -32,7 +32,7 @@
     SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.*
     FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
     WHERE ml.menu_name = '%s'
-    ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC";
+    ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC, p10 ASC, p11 ASC, p12 ASC, p13 ASC, p14 ASC, p15 ASC";
   $result = db_query($sql, $menu['menu_name']);
   $tree = menu_tree_data($result);
   $node_links = array();
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.238.2.3
diff -u -r1.238.2.3 system.install
--- modules/system/system.install	25 Apr 2008 21:24:20 -0000	1.238.2.3
+++ modules/system/system.install	14 Aug 2008 11:40:42 -0000
@@ -928,6 +928,42 @@
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0),
+      'p10' => array(
+        'description' => t('The ninth mlid in the materialized path. See p1.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'p11' => array(
+        'description' => t('The ninth mlid in the materialized path. See p1.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'p12' => array(
+        'description' => t('The ninth mlid in the materialized path. See p1.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'p13' => array(
+        'description' => t('The ninth mlid in the materialized path. See p1.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'p14' => array(
+        'description' => t('The ninth mlid in the materialized path. See p1.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'p15' => array(
+        'description' => t('The ninth mlid in the materialized path. See p1.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
       'updated' => array(
         'description' => t('Flag that indicates that this link was generated during the update from Drupal 5.'),
         'type' => 'int',
@@ -940,7 +976,7 @@
       'menu_plid_expand_child' => array(
         'menu_name', 'plid', 'expanded', 'has_children'),
       'menu_parents' => array(
-        'menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'),
+        'menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11', 'p12', 'p13', 'p14', 'p15'),
       'router_path' => array(array('router_path', 128)),
       ),
     'primary key' => array('mlid'),
@@ -1681,12 +1717,18 @@
       'p7'           => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
       'p8'           => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
       'p9'           => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+      'p10'          => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+      'p11'          => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+      'p12'          => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+      'p13'          => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+      'p14'          => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
+      'p15'          => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0),
       'updated'      => array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'size' => 'small'),
     ),
     'indexes' => array(
       'path_menu'              => array(array('link_path', 128), 'menu_name'),
       'menu_plid_expand_child' => array('menu_name', 'plid', 'expanded', 'has_children'),
-      'menu_parents'           => array('menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'),
+      'menu_parents'           => array('menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11', 'p12', 'p13', 'p14', 'p15'),
       'router_path'            => array(array('router_path', 128)),
     ),
     'primary key' => array('mlid'),

Alan Davison
www.caignwebs.com.au

Alan Davison