| 7 taxonomy.module | taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, |
| 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, |
| 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.
$depth: Internal use only. Now deprecated and isn't used. It is left here only because of compatibility issues.
$max_depth: The number of levels of the tree to return. Leave NULL to return all levels.
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.
- forum_get_forums in modules/
forum/ forum.module - Returns a list of all forums for a given taxonomy id
- forum_nodeapi in modules/
forum/ forum.module - Implementation of hook_nodeapi().
- taxonomy_check_vocabulary_hierarchy in modules/
taxonomy/ taxonomy.module - Dynamically check and update the hierarachy flag of a vocabulary. Checks and updates the hierarchy flag of a vocabulary.
- taxonomy_form_all in modules/
taxonomy/ taxonomy.module - Generate a set of options for selecting a term from all vocabularies.
- taxonomy_form_term in modules/
taxonomy/ taxonomy.admin.inc - Form function for the term edit form.
File
- modules/
taxonomy/ taxonomy.module, line 848 - Enables the organization of content into categories.
Code
function taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL) {
static $children, $parents, $terms;
// 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();
$parents[$vid] = array();
$terms[$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 = (!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 = $terms[$vid][$child];
if (count($parents[$vid][$term->tid]) > 1) {
// We have a term with multi parents here. Clone the term,
// so that the depth attribute remains correct.
$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
Doesn't return nested arrays
PermalinkKeep in mind that taxonomy_get_tree doesn't actually return a tree of nested arrays - it returns a flat array of terms, with terms having a "parents" attribute.
but it's in order
PermalinkI was scratching my head over this one because what this function returns is not at all a hierarchical representation of the tree of terms -- unless you know that a parent term is followed by its children in the array you get back. Which they are, although it doesn't say so here in the documentation. So that means you don't have to recursively process this function's output in order to get a nested list, which you would need to do otherwise.
Check it
Permalink- non recursive
- one pass
- resistant for non in order positions of terms
- safe for more than one parent
... function:
<?phpfunction _taxonomy_get_real_tree($vid){
$result_tree = array();
$terms = array();
foreach(taxonomy_get_tree($vid) as $term){
if(isset($terms[$term->tid])){
$term->children = $terms[$term->tid]->children;
$terms[$term->tid] = $term;
}else{
$terms[$term->tid] = $term;
}
if(
$term->depth === 0){$result_tree[$term->tid] = &$terms[$term->tid];
continue;
}
foreach(
$term->parents as $tid){if($tid){
if(!isset($terms[$tid])){
$terms[$tid] = new stdClass();
}
$terms[$tid]->children[$term->tid] = &$terms[$term->tid];
}
}
}
return $result_tree;
}
?>
Work for me.
This function gets an more useful tree
PermalinkIn my company, we make a function to get an more useful tree, with "nesteds" terms in an array that you can access with the $term->children.
Note: This function don't works as expected if you want to use it with unlimited depth. See the comment from mbmasuda below to get the correct function.
<?phpfunction taxonomy_get_nested_tree($terms = array(), $max_depth = NULL, $parent = 0, $parents_index = array(), $depth = 0) {
if (is_int($terms)) {
$terms = taxonomy_get_tree($terms);
}
foreach(
$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;}
?>
Use them with:
<?php$tree = taxonomy_get_nested_tree(7); // Where 7 is your vocabulary id (vid)
?>
Or with a depth limit (starting from 0), as:
<?php$tree = taxonomy_get_nested_tree(7, 1); // Where 1 is the depth limit
?>
Thanks for the function, it's really helpful!
PermalinkThank you for posting your taxonomy_get_nested_tree function. Unfortunately it did not work out of the box when given only one argument—in that case it only returned the top level items and no children. I fixed it so now it works as described when $max_depth is null.
<?phpfunction taxonomy_get_nested_tree($terms = array(), $max_depth = NULL, $parent = 0, $parents_index = array(), $depth = 0) {
if (is_int($terms)) {
$terms = taxonomy_get_tree($terms);
}
foreach(
$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;}
?>
Output function using Drupal's menu themeing functions
PermalinkHere's a function that tries to mimic the default menu output. $tree parameter uses output from mbmasuda's taxonomy_get_nested_tree() above.
<?php
function taxonomy_nested_tree_output($tree) {
if (!empty(
$tree)) {$output = '';
$q = $_GET['q'];
$num_items = count($tree);
$i = 0;
foreach (
$tree as $tid => $term) {$extra_classes = array();
$in_active_trail = FALSE;
$data = array(
'title' => $term->name,
'href' => 'taxonomy/term/'. $tid,
);
if ($data['href'] == $q) {
$data['localized_options'] = array('attributes' => array('class' => 'active'));
$in_active_trail = TRUE;
$extra_classes[] = 'active';
}
if ($i == 0) {
$extra_class[] = 'first';
}
if ($i == $num_items - 1) {
$extra_classes[] = 'last';
}
$link = theme('menu_item_link', $data);
$has_children = $term->children ? TRUE : FALSE;
if ($has_children) {
foreach($term->children as $child_tid => $child) {
if ('taxonomy/term/'. $child_tid == $q) {
$in_active_trail = TRUE;
}
}
$output .= theme('menu_item', $link, $has_children, taxonomy_nested_tree_output($term->children), $in_active_trail, implode(' ', $extra_classes));
}
else {
$output .= theme('menu_item', $link, $has_children, '', $in_active_trail, implode(' ', $extra_classes));
}
$i++;
}
}
return
theme('menu_tree', $output);}
?>
Theming taxonomy_get_nested_tree() into a menu
PermalinkI used the array that taxonomy_get_nested_tree() returns, and themed it into a menu. I did this because I couldn't figure out how to use the taxonomy_menu module to create a dynamic unordered hierarchal menu, each item linking to the current path with a custom query string. My custom code gives links with the path of /[current_path]?category=[tid]+[tid]+....+[tid]
Here's the code I used:
$parents = taxonomy_get_nested_tree(1);$path = $_GET['q'];
$menu_tree = '<ul class="menu">';
for($i = 1; $i <= count($parents); $i++) {
//create a list item, assign it a class of "first" or "last" and give it the proper path and query string
$menu_tree.='<li'.($i == 1 ? ' class="first"' : '').($i == count($parents) ? ' class="last"':'').'>'.l($parents[$i]->name,$path,array('query'=>'category='.$parents[$i]->tid));
if($parents[$i]->children){
//if it has children, build a nested list in this <li>
$menu_tree.='<ul>';
$ii = 1;
foreach($parents[$i]->children as $level_1 ) {
$menu_tree .= '<li'.($ii == 1 ? ' class="first"' : '').($ii == count($parents[$i]->children) ? ' class="last"':'').'>'.l($level_1->name,$path,array('query'=>'category='.$parents[$i]->tid.'+'.$level_1->tid));
if($level_1->children){
$menu_tree .= '<ul>';
$iii = 1;
foreach($level_1->children as $level_2) {
$menu_tree .= '<li'.($iii == 1 ? ' class="first"' : '').($iii == count($level_1->children) ? ' class="last"':'').'>'.l($level_2->name,$path,array('query'=>'category='.$parents[$i]->tid.'+'.$level_1->tid.'+'.$level_2->tid)).'</li>';
$iii++;
}
$menu_tree .='</ul>';
}
$menu_tree .= '</li>';
$ii ++;
}
$menu_tree .= '</ul>';
}
}
return $menu_tree;
I only built this out for a vocabulary with 3 tiers, and I'm sure there would be a way of recursively processing it so you don't need a new foreach statement on each successive level... but this works for me.
recursively display nested tree
Permalink<?phpfunction theme_taxonomy_nested_tree($tree) {
if (count($tree)) {
$output = '<ul class="taxonomy-tree">';
foreach ($tree as $term) {
$output .= '<li class="taxonomy-term">';
$output .= l($term->name, taxonomy_term_path($term));
if ($term->children) {
$output .= theme('illogica_category_tree', $term->children);
}
$output .= '</li>';
}
$output .= '</ul>';
}
return $output;
}
?>
Wrong code?
PermalinkSomeone filed an issue reporting that this doesn't work:
http://drupal.org/node/1046542
Just posting a link in case someone is interested.
Return values for current node
PermalinkDoes anyone know of a way in which it might possible to use this snippet in combination with Views (or an argument in the page.tpl.php) so that I can return a taxonomy hierarchy for items sharing the same tag or a child tag of the current node?
I would like to output this on the bottom of each of my nodes as "also in this section" links.
I have them listed with views at the moment, but they are not nested which I need for accessibility and to style the children & parents differently.
Thanks
Get next term in the tree
PermalinkIs there a way to get the next or the previous term of the current term of a node?
Thanks
Terms are output twice if they have two parents - with a twist
PermalinkWorth noting.
If a term has two parents then it is actually output twice. The list of terms output seems to match exactly the list produced under admin/content/taxonomy/[vid].
With a twist - the value of $depth is calculated by the taxonomy_get_tree() function based on how far down the tree it needs to recurse. So if a term has two parents which are at different depths then the two term records output will differ in the value of the $depth attribute.
This might help someone to understand this function's behaviour.
'Results are statically cached'
Permalink'Results are statically cached' and there is no parameter to turn this off.
This means that it's important to be careful when calling this function as once it's been called the first time the results are effectively 'cached'.
It looks like that in D7 this has been added - so I doubt this will be added to this version.
Add in a reset flag
PermalinkThis may be the basis for an updated function which has a reset value.
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, taxonomy_get_tree_with_reset($vid, $child, $depth, $max_depth));
}
}
}
return $tree;
}
My solution
PermalinkThis gives you a nested taxonomy list. Every level has it's own class!
<?php
function list(){
$tree = taxonomy_get_nested_tree(4);
return theme_taxonomy_nested_tree($tree);
}
function
taxonomy_get_nested_tree($terms = array(), $max_depth = NULL, $parent = 0, $parents_index = array(), $depth = 0) {if (is_int($terms)) {
$terms = taxonomy_get_tree($terms);
}
foreach(
$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
theme_taxonomy_nested_tree($tree) {if (count($tree)) {
$output = '<ul>';
foreach ($tree as $term) {
$output .= '<li class="taxonomy-term">';
$output .= l($term->name, taxonomy_term_path($term),array('html'=>true,'attributes'=>array('class'=>'level-'.$term->depth)));
if (!empty($term->children)){
$output .= as_get_children($term->children);
}
$output .= '</li>';
}
$output .= '</ul>';
}
return $output;
}
function
get_children($term){$output = '<ul>';
foreach($term as $child){
$output .= "<li>";
$output .= l($child->name,taxonomy_term_path($child),array('html'=>true,'attributes'=>array('class'=>'level-'.$child->depth)));
$output .= "</li>";
if(!empty($child->children)){
$output .=get_children($child->children);
}
}
$output .= '</ul>';
return
$output;}
?>