function taxonomy_get_tree

You are here

7 taxonomy.module taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $load_entities = FALSE)
4.6 taxonomy.module taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL)
4.7 taxonomy.module taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL)
5 taxonomy.module taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL)
6 taxonomy.module taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL)
8 taxonomy.module taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $load_entities = FALSE)

Create a hierarchical representation of a vocabulary.

Parameters

$vid: Which vocabulary to generate the tree for.

$parent: The term ID under which to generate the tree. If 0, generate the tree for the entire vocabulary.

$max_depth: The number of levels of the tree to return. Leave NULL to return all levels.

$load_entities: If TRUE, a full entity load will occur on the term objects. Otherwise they are partial objects queried directly from the {taxonomy_term_data} table to save execution time and memory consumption when listing large numbers of terms. Defaults to FALSE.

Return value

An array of all term objects in the tree. Each term object is extended to have "depth" and "parents" attributes in addition to its normal ones. Results are statically cached. Term objects will be partial or complete depending on the $load_entities parameter.

13 calls to taxonomy_get_tree()
ForumTestCase::testAddOrphanTopic in modules/forum/forum.test
Tests that forum nodes can't be added without a parent.
forum_forum_load in modules/forum/forum.module
Returns a tree of all forums for a given taxonomy term ID.
hook_options_list in modules/field/modules/options/options.api.php
Returns the list of options to be displayed for a field.
NodeAccessPagerTestCase::testForumPager in modules/node/node.test
Tests the forum node pager for nodes with multiple grants per realm.
TaxonomyTermFunctionTestCase::testTaxonomyVocabularyTree in modules/taxonomy/taxonomy.test
Test a taxonomy with terms that have multiple parents of different depths.

... See full list

2 string references to 'taxonomy_get_tree'
TaxonomyTermTestCase::testTermReorder in modules/taxonomy/taxonomy.test
Save, edit and delete a term using the user interface.
taxonomy_terms_static_reset in modules/taxonomy/taxonomy.module
Clear all static cache variables for terms.

File

modules/taxonomy/taxonomy.module, line 1110
Enables the organization of content into categories.

Code

function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $load_entities = FALSE) {
  $children = &drupal_static(__FUNCTION__, array());
  $parents = &drupal_static(__FUNCTION__ . ':parents', array());
  $terms = &drupal_static(__FUNCTION__ . ':terms', array());

  // We cache trees, so it's not CPU-intensive to call taxonomy_get_tree() on a
  // term and its children, too.
  if (!isset($children[$vid])) {
    $children[$vid] = array();
    $parents[$vid] = array();
    $terms[$vid] = array();

    $query = db_select('taxonomy_term_data', 't');
    $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
    $result = $query->addTag('translatable')->addTag('term_access')->fields('t')->fields('h', array('parent'))->condition('t.vid', $vid)->orderBy('t.weight')->orderBy('t.name')->execute();

    foreach ($result as $term) {
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
    }
  }

  // Load full entities, if necessary. The entity controller statically
  // caches the results.
  if ($load_entities) {
    $term_entities = taxonomy_term_load_multiple(array_keys($terms[$vid]));
  }

  $max_depth = (!isset($max_depth)) ? count($children[$vid]) : $max_depth;
  $tree = array();

  // Keeps track of the parents we have to process, the last entry is used
  // for the next processing step.
  $process_parents = array();
  $process_parents[] = $parent;

  // Loops over the parent terms and adds its children to the tree array.
  // Uses a loop instead of a recursion, because it's more efficient.
  while (count($process_parents)) {
    $parent = array_pop($process_parents);
    // The number of parents determines the current depth.
    $depth = count($process_parents);
    if ($max_depth > $depth && !empty($children[$vid][$parent])) {
      $has_children = FALSE;
      $child = current($children[$vid][$parent]);
      do {
        if (empty($child)) {
          break;
        }
        $term = $load_entities ? $term_entities[$child] : $terms[$vid][$child];
        if (isset($parents[$vid][$term->tid])) {
          // Clone the term so that the depth attribute remains correct
          // in the event of multiple parents.
          $term = clone $term;
        }
        $term->depth = $depth;
        unset($term->parent);
        $term->parents = $parents[$vid][$term->tid];
        $tree[] = $term;
        if (!empty($children[$vid][$term->tid])) {
          $has_children = TRUE;

          // We have to continue with this parent later.
          $process_parents[] = $parent;
          // Use the current term as parent for the next iteration.
          $process_parents[] = $term->tid;

          // Reset pointers for child lists because we step in there more often
          // with multi parents.
          reset($children[$vid][$term->tid]);
          // Move pointer so that we get the correct term the next time.
          next($children[$vid][$parent]);
          break;
        }
      } while ($child = next($children[$vid][$parent]));

      if (!$has_children) {
        // We processed all terms in this hierarchy-level, reset pointer
        // so that this function works the next time it gets called.
        reset($children[$vid][$parent]);
      }
    }
  }

  return $tree;
}

Comments

Structure of print_r($results), circa Drupal 7:

Array
(
    [0] => stdClass Object
        (
            [tid] => 1
            [vid] => 2
            [name] => About Us
            [description] =>
            [format] => wysiwyg_editor
            [weight] => 0
            [depth] => 0
            [parents] => Array
                (
                    [0] => 0
                )

        )

    [1] => stdClass Object
        (
            [tid] => 2
            [vid] => 2
            [name] => Stories
            [description] =>
            [format] => wysiwyg_editor
            [weight] => 0
            [depth] => 0
            [parents] => Array
                (
                    [0] => 0
                )

        )

)

Some fields above may be from contributed modules and not included in the default, but the structure should still be useful for reference.

Thanks.
It saves my time....

In case anyone needs to build a taxonomy menu (block) using this function here's one way to do it:

<?php
function mymodule_block_info() {
 
$blocks['taxonomy_menu'] = array(
   
'info' => t('Taxonomy menu'),
  );

  return

$blocks;
}

function

mymodule_block_view($delta = '') {
 
$block = array();
 
 
// Taxonomy menu block.
 
if ($delta == 'taxonomy_menu') {
   
$terms = taxonomy_get_tree(1); // Use the correct vocabulary id.
   
    // Get the active trail tid-s.
   
$active = arg(2);
   
$active_parents = taxonomy_get_parents_all($active);
   
$active_parents_tids = array();
    foreach (
$active_parents as $parent) {
     
$active_parents_tids[] = $parent->tid;
    }
   
   
// Build the menu.
   
$term_count = count($terms);
   
$cont = '<ul class="taxonomy_menu">';
    for (
$i = 0; $i < $term_count; $i++) {
     
// Build the classes string.
     
$classes = '';
     
$children = taxonomy_get_children($terms[$i]->tid);
     
$active_trail = in_array($terms[$i]->tid, $active_parents_tids);
      if (
$active_trail && $children) $classes .= 'expanded active-trail ';
      elseif (
$active_trail) $classes .= 'active-trail ';
      elseif (
$children) $classes .= 'collapsed ';
     
      if (
$i == 0) $cont .= '<li class="first '.$classes.'">'.l($terms[$i]->name, 'taxonomy/term/'.$terms[$i]->tid);
      else {
        if (
$terms[$i]->depth == $depth) $cont .= '</li><li class="'.$classes.'">'.l($terms[$i]->name, 'taxonomy/term/'.$terms[$i]->tid);
        elseif (
$terms[$i]->depth > $depth) $cont .= '<ul class="level-'.$terms[$i]->depth.'"><li class="first '.$classes.'">'.l($terms[$i]->name, 'taxonomy/term/'.$terms[$i]->tid);
        elseif (
$terms[$i]->depth < $depth) {
         
// Add missing end-tags depending of depth level difference.
         
for ($j = $terms[$i]->depth; $j < $depth; $j++) {
           
$cont .= '</li></ul>';
          }
         
$cont .= '</li><li class="'.$classes.'">'.l($terms[$i]->name, 'taxonomy/term/'.$terms[$i]->tid);
        }
       
// If we have reached the last element add all possibly missing end-tags.
       
if (!isset($terms[$i+1])) {
          for (
$j = 0; $j < $terms[$i]->depth; $j++) {
           
$cont .= '</li></ul>';
          }
        }
      }
     
$depth = $terms[$i]->depth;
    }
   
$cont .= '</li></ul>';
   
   
// Set the menu html as block content.
   
$block['content'] = array('#markup' => $cont);
  }
 
  return
$block;
}
?>

In your css file you can use 'ul.taxonomy_menu li.collapsed ul {display: none;}' to make it collapsible. Note that the optional $parent, $max_depth, $load_entities parameters (when calling taxonomy_get_tree) allow you to further customize the menu to fit your needs.

This looks quite what I need for a menu built based on a taxonomy. Can you tell me, how I can implement the code? In the content field of a custom block?
I don´t get no output that way.

Check out the taxonomy menu and menu block modules to see whether they already offer what you want (especially if you are not familiar with coding in Drupal).

If customization beyond what those modules offer is needed then this code might be helpful, but you need to create a custom module where the code goes into. You can check out this and this page to get started. Once the base is built put the code inside mymodule.module file (all 'mymodule' instances need to be replaced with your module's name), enable the module and check the block administration page. If it's not there make sure you got everything right and clear the caches.

This was just what I needed! I'm new to Drupal development and I was pretty daunted by the prospect of figuring out how to replicate WordPress' wp_list_categories(). You've saved me a lot of trouble!

$vid = taxonomy_vocabulary_machine_name_load("XXX")->vid;
$terms = taxonomy_get_tree($vid);

This is a pretty handy way to find the vid as well:
drush php-eval '$tax=taxonomy_vocabulary_machine_name_load("XXX"); echo $tax->vid;'

Once can use

<?php
/**
* returns a array for use with #options in a form field
* */
function taxonomy_options_array($machine_name) {
 
$v = taxonomy_vocabulary_machine_name_load($machine_name);
 
$terms = taxonomy_get_tree($v->vid);
  foreach (
$terms as $term) {
   
$options[$term->tid] = $term->name;
  }
  return
$options;
}
?>

(maintained at https://gist.github.com/1754684)

For hierarchical simple select (my mod taken from /modules/taxonomy/taxonomy.admin.inc)

<?php
/**
* Returns a array for use with #options in a form field
**/
function _taxonomy_options($machine_name) {
 
$vocabulary = taxonomy_vocabulary_machine_name_load($machine_name);
 
$tree = taxonomy_get_tree($vocabulary->vid);
  foreach (
$tree as $item) {
   
$options[$item->tid] = str_repeat('-', $item->depth) . $item->name;
  }
  return
$options;
}
?>

if you have 35 000 terms in voc.
$t_t = taxonomy_get_tree(2, $parent = 0, $max_depth = 1, $load_entities = FALSE);
this line takes 140 ms!!! Think well before you use it in your scripts, this is the reason for me to abandon it and write my own.

Can you share the function you've written? I'm looking to make a hierarchical list of my own entity type (not taxonomy) and I'm trying to find the most efficient approach because I expect a lot of entities.

I want to do first vocabulary term to be parent of second vocabulary term . please help me .

In D6 I couldn't use taxonomy_get_tree() repeatedly in the same code block because of the static caching - this means that this function can only be called once which is a pain.

So I modified the function to include the use of a reset flag to enable the statics to be reset.

NB - This is my function which was a modified version of the D6 taxonomy_get_tree() - I think the same is needed for D7.

function taxonomy_get_tree_with_reset($vid, $parent = 0, $depth = -1, $max_depth = NULL, $reset = FALSE) {
  static $children, $parents, $terms;

  if ($reset) {
    unset($children[$vid]);
    unset($parents[$vid]);
    unset($terms[$vid]);
  }

  $depth++;

  // We cache trees, so it's not CPU-intensive to call get_tree() on a
  // term and its children, too.
  if (!isset($children[$vid])) {
    $children[$vid] = array();

    $result = db_query(db_rewrite_sql('SELECT t.tid, t.*, parent FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d ORDER BY weight, name', 't', 'tid'), $vid);
    while ($term = db_fetch_object($result)) {
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
    }
  }

  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
  $tree = array();
  if ($max_depth > $depth && !empty($children[$vid][$parent])) {
    foreach ($children[$vid][$parent] as $child) {
      $term = drupal_clone($terms[$vid][$child]);
      $term->depth = $depth;

      // The "parent" attribute is not useful, as it would show one
      // parent only.
      unset($term->parent);
      $term->parents = $parents[$vid][$child];
      $tree[] = $term;
      if (!empty($children[$vid][$child])) {
        $tree = array_merge($tree, data_export_import_taxonomy_get_tree_with_reset($vid, $child, $depth, $max_depth));
      }
    }
  }
  return $tree;
}

Drupal 7:

I would like to have more consistency here in the API. For example 50% of the taxonomy methods work with the machine_name as an argument and the other half insists on getting the vid.

I'm an old school Drupaler so I like to provide the vid. But then there is the UI team which seem to hate the coders and hides the vid in the admin UI very very well. You have to go into the database to actually find which vid has a certain vocabulary, so that you can write code.

That is not nice. There should be a mode in the Drupal UI which displays stuff for module writers. For example the and ID row in evry admin table would be a good start.

Hello people.
I need to do with this function DRUPAL7 made ​​for Drupal (http://drupal.org/node/225426) View with taxonomy arguments, presented all on the same page: Terms as headlines, displayed hierarchically
I'm not a programmer, I would ask if anyone can help with Drupal 7 to a list categorized by the taxonomy term.
In drupal 6 I followed this and it turned out perfect
I can not pass this code to make it with Drupal-7

snipe for drupal 6 ( http://drupal.org/node/225426)

<?php
/* Enter the ID of the vocabulary which terms you want to show as headlines */
$vocabulary_id = "5";

/*Enter the name of the view that should show the nodes */
$view_name = "VIEW_NAME";

$founddepth = 0;
$output = '';

foreach(

taxonomy_get_tree($vocabulary_id,0,-1,1) as $parent) {
   
$append1 = '';
   
$append2 = '';
   
$append3 = '';
   
$viewcontent = views_embed_view($view_name, 'default', $parent->name);
   
$viewchildren = taxonomy_get_children($parent->tid);
   
$append1 = '<div class="h3"><h3>' . t($parent->name) . '</h3>';
   
$append1 .= $viewcontent;
    if (
$viewchildren ) {
       
$append2 = '<div class="h4">';
        foreach(
taxonomy_get_children($parent->tid) as $child ) {
           
$viewcontent = views_embed_view($view_name, 'default', $child->name);
           
$viewchildren = taxonomy_get_children($child->tid);
           
$append2 .= '<h4>' . t($child->name) . '</h4>';
           
$append2 .= $viewcontent;
            if (
$viewchildren ) {
               
$append3 = '<div class="h5">';
                foreach(
taxonomy_get_children($child->tid) as $child2 ) {
                   
$viewcontent = views_embed_view($view_name, 'default', $child2->name);
                    if ( !
stristr($viewcontent,'view-content') ) continue;
                   
$append3 .= '<h5>' . t($child2->name) . '</h5>';
                   
$append3 .= $viewcontent;
                }
               
$append3 .= '</div>';
            }
            if (!
stristr($append3,'view-content')) $append3 = '';
           
$append2 .= $append3;
        }
       
$append2 .= '</div>';
    }
    if (!
stristr($append2,'view-content')) $append2 = '';
   
$append1 .= $append2.'</div>';
    if (!
stristr($append1,'view-content')) $append1 = '';

   

$output .= $append1;
}

print

$output;
?>

From already thank you very much
Sorry for my English

This was awesome Dooshta. Thanks for posting.

Damn this don't provide attached fields values

<?php
function taxonomy_get_nested_tree($vid_or_terms = array(), $max_depth = NULL, $parent = 0, $parents_index = array(), $depth = 0) {

  if (!

is_array($vid_or_terms)) {
   
$vid_or_terms = taxonomy_get_tree($vid_or_terms);
  }

  foreach (

$vid_or_terms as $term) {

    foreach (

$term->parents as $term_parent) {
      if (
$term_parent == $parent) {
       
$return[$term->tid] = $term;
      }
      else {
       
$parents_index[$term_parent][$term->tid] = $term;
      }
    }
  }

  foreach (

$return as &$term) {
    if (isset(
$parents_index[$term->tid]) && (is_null($max_depth) || $depth < $max_depth)) {
     
$term->children = taxonomy_get_nested_tree($parents_index[$term->tid], $max_depth, $term->tid, $parents_index, $depth + 1);
    }
  }

  return

$return;
}

function

taxonomy_nested_tree_render($tree, $recurring = FALSE) {

 

$items = array();

  if (

count($tree)) {

    foreach (

$tree as $term) {
     
$path = taxonomy_term_uri($term);
     
$item = array('data' => l($term->name, $path["path"]));

      if (isset(

$term->children)) {
       
$item["children"] = taxonomy_nested_tree_render($term->children, TRUE);
      }
     
$items[] = $item;
    }

  }

  if (

$recurring) {
    return
$items;
  }

  return array(
   

'#theme' => 'item_list',
   
'#items' => $items,
   
'#attributes' => array('class' => 'taxonomy-tree'),
  );
}
?>

Usage:

<?php
$voc
= taxonomy_vocabulary_machine_name_load('categoria');
$tree = taxonomy_get_nested_tree($voc->vid);
return
taxonomy_nested_tree_render($tree);
?>

I have used the script in https://api.drupal.org/comment/22553#comment-22553 and changed it

The new script ignores the menu items with no products. It also uses parents field instead of depth to create the menu. This allows easy theming.

/**
* Callback for Import CSV to upload csv file (ielts test dates) to the server
*/
function menu_custom_generate() {

$terms = taxonomy_get_tree(5, 0, 2); // Use the correct vocabulary id.
//print_r($terms);
// Get the active trail tid-s.
$active = arg(2);
$active_parents = taxonomy_get_parents_all($active);
$active_parents_tids = array();
foreach ($active_parents as $parent) {
$active_parents_tids[] = $parent->tid;
}

// Build the menu.
$term_count = count($terms);
$cont = '';
$cont .= '

';
}

}
$cont .= '

';

return $cont;

}
function total_items_term($tid)
{

$result = db_query('SELECT n.nid FROM taxonomy_index rd INNER JOIN node n ON rd.nid = n.nid WHERE rd.tid = :tid AND n.status = 1 ', array(':tid' => $tid));
$count = count($result->fetchCol());
return $count;

}

This function will add an array of the children in each term. It takes advantage of taxonomy_get_tree returning the results in a sorted array for better performance. If anyone knows of a better way of doing it please share :)

<?php
function onix_reportes_build_tree($flatTree){
   
$tree = array();
   
$last_term_stack = array();
    foreach (
$flatTree as &$term) {
        while(
count($last_term_stack) > 0){
            if(
in_array(end($last_term_stack)->tid, $term->parents)) {
                   
$lastTerm = end($last_term_stack);
                   
$lastTerm->children[]=$term;
                   
array_push($last_term_stack,$term);
                    break;
                } else {
                   
array_pop($last_term_stack);
                }
        }
        if(empty(
$last_term_stack)){
           
$tree[] =$term;
           
array_push($last_term_stack, $term);
        }
    }
    return
$tree;
}
?>

Hi sorry i am new to code, please i would like the menu to be not collapsed, there is any way to do that. i would like to be dropdown list. Thank you

Hi,
I need to get the terms value without childrens, right now I'm using this

<?php
        $terms
=taxonomy_get_tree(2)
        foreach(
$terms as $key=>$term) {
            if(
$term->parents[0]==0) {
                echo
' term name '. $term->name ;
            }
        }
   
?>

Any other way to get the parent values only?
Thanks.

Alright, you guys seem to know what you're talking about!

I'm trying to create a script that gives me a taxonomy term, based on the current user. For example, the user "Peter" has a taxonomy term associated with his user account, such as "Expert" from a vocabulary "Levels" - Expert was selected from a dropdown when his user account was created, so it's already in the DB.

Now, whenever a user like Peter creates a new piece of content, I want to pull the taxonomy term that's associated with his account and pre-populate a custom text field that only admins have access to, not users like Peter and insert his appropriate term.. How would I go about doing that? Is it appropriate to use the taxonomy_get_tree() for something like this? I'm using the Computed Field module.