Programatically creating a nested taxonomy list in Drupal 7

Say you have a taxonomy hierarchy in which you would like to create a nested list.  While it is possible to loop through a taxonomy tree and create the html markup using a number of conditional statements, this is far from ideal and prone to error.  I much prefer to use Drupal's theme functions whenever possible to generate html from a render array.  In this demonstration, we'll grab all taxonomy terms in a vocabulary and use some recursion to construct manageable data structures that we can then leave to Drupal to generate all the html.  

Example of taxonomy hierarchy fully expanded called "test_vocab":

 
  •  
    •  
      •  
  •  
    •  
      •  

 

Here's the callback function we're going to use to return the themed output.

 function example_callback_function_of_nested_list_vocab() {
  $cat_name = 'test_vocab';  // The vocabulary name
  $tree = taxonomy_get_tree(taxonomy_vocabulary_machine_name_load($cat_name)->vid); // $tree is an array of all the terms in the vocabulary
  $term_tree = array();
  $item_list = array();
    
  // Here we restructure this a bit so we can work with array keys.  Only necessary to streamline the logic necessary in the recursive functions
  foreach ($tree as $term) {
    $term_tree[$term->tid] = $term;
  }
  _construct_nested_taxonomy_array($term_tree); // takes the flat array of terms and nest terms underneath their parent items
  $term_tree = array_reverse($term_tree);

  _construct_nested_taxonomy_item_list($term_tree, $item_list); //generates the list items that are expected by theme_item_list (https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_item_list/7)

  return theme('item_list', array('items' => $item_list);;
} 

The next step is to build the "_construct_nested_taxonomy_array" function that takes the flat array of terms in the vocabulary and turns it into a nested structure.  This requires a bit of recursion to allow for infinite depth.

 function _construct_nested_taxonomy_array(&$tree){
  foreach (array_reverse($tree) as $term) {
    if(array_key_exists($term->parents[0], $tree)){
      $tree[$term->parents[0]]->children[$term->tid] = $term;
      _construct_nested_taxonomy_array($tree[$term->parents[0]]->children);
      unset($tree[$term->tid]);
    }
  }
} 

Finally, we need to build the "_construct_nested_taxonomy_item_list" function that takes the nested data structure we created in "_construct_nested_taxonomy_array" and turns it into what is expected by Drupal's theme item list function: https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_item_list/7.

 function _construct_nested_taxonomy_item_list(&$tree, &$item_list){
  foreach (array_reverse($tree) as $term) {
    $item_list[$term->tid] = array('data' => $term->name);
    if($term->children){
      _construct_nested_taxonomy_item_list($term->children, $item_list[$term->tid]['children']);
    }
  }
} 

There you have it.  The "example_callback_function_of_nested_list_vocab" callback we created outputs the structure as we defined in the vocabulary hierarchy, and we didn't need to riddle our callback with any nasty logic to open and close html tags.  In case you were thinking "but my nested list is special and requires special html", we can use Drupal's theme hooks for overriding the default "theme_item_list".

radial gradient rings

Ready to get started?

Let's set up a Zoom meeting to talk about your project, or better yet, Starbucks and a walk around Green Lake!