Index: modules/field/modules/options/options.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/options/options.api.php,v retrieving revision 1.1 diff -u -p -r1.1 options.api.php --- modules/field/modules/options/options.api.php 14 Dec 2009 20:18:55 -0000 1.1 +++ modules/field/modules/options/options.api.php 3 Sep 2010 15:03:37 -0000 @@ -53,7 +53,7 @@ function hook_options_list($field) { // on properties of the field. Example from taxonomy.module: $options = array(); foreach ($field['settings']['allowed_values'] as $tree) { - $terms = taxonomy_get_tree($tree['vid'], $tree['parent']); + $terms = taxonomy_get_entries($tree['vid'], $tree['parent']); if ($terms) { foreach ($terms as $term) { $options[$term->tid] = str_repeat('-', $term->depth) . $term->name; Index: modules/forum/forum.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/forum/forum.admin.inc,v retrieving revision 1.36 diff -u -p -r1.36 forum.admin.inc --- modules/forum/forum.admin.inc 8 Aug 2010 19:21:25 -0000 1.36 +++ modules/forum/forum.admin.inc 3 Sep 2010 15:03:38 -0000 @@ -286,7 +286,7 @@ function _forum_parent_select($tid, $tit } $vid = variable_get('forum_nav_vocabulary', ''); - $children = taxonomy_get_tree($vid, $tid); + $children = taxonomy_get_entries($vid, $tid); // A term can't be the child of itself, nor of its children. foreach ($children as $child) { @@ -294,7 +294,7 @@ function _forum_parent_select($tid, $tit } $exclude[] = $tid; - $tree = taxonomy_get_tree($vid); + $tree = taxonomy_get_entries($vid); $options[0] = '<' . t('root') . '>'; if ($tree) { foreach ($tree as $term) { Index: modules/forum/forum.module =================================================================== RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v retrieving revision 1.573 diff -u -p -r1.573 forum.module --- modules/forum/forum.module 17 Aug 2010 16:20:07 -0000 1.573 +++ modules/forum/forum.module 3 Sep 2010 15:03:40 -0000 @@ -777,7 +777,7 @@ function forum_forum_load($tid = NULL) { // Load the tree below. $forums = array(); - $_forums = taxonomy_get_tree($vid, $tid); + $_forums = taxonomy_get_entries($vid, $tid); if (count($_forums)) { $query = db_select('node', 'n'); Index: modules/taxonomy/taxonomy.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.admin.inc,v retrieving revision 1.107 diff -u -p -r1.107 taxonomy.admin.inc --- modules/taxonomy/taxonomy.admin.inc 31 Jul 2010 13:01:50 -0000 1.107 +++ modules/taxonomy/taxonomy.admin.inc 3 Sep 2010 15:03:41 -0000 @@ -286,7 +286,7 @@ function taxonomy_overview_terms($form, $delta = 0; $term_deltas = array(); - $tree = taxonomy_get_tree($vocabulary->vid); + $tree = taxonomy_get_entries($vocabulary->vid); $term = current($tree); do { // In case this tree is completely empty. @@ -462,7 +462,7 @@ function taxonomy_overview_terms_submit( $hierarchy = 0; // Update the current hierarchy type as we go. $changed_terms = array(); - $tree = taxonomy_get_tree($vocabulary->vid); + $tree = taxonomy_get_entries($vocabulary->vid); if (empty($tree)) { return; @@ -721,13 +721,13 @@ function taxonomy_form_term($form, &$for '#weight' => 10, ); - // taxonomy_get_tree and taxonomy_get_parents may contain large numbers of + // taxonomy_get_entries and taxonomy_get_parents may contain large numbers of // items so we check for taxonomy_override_selector before loading the // full vocabulary. Contrib modules can then intercept before // hook_form_alter to provide scalable alternatives. if (!variable_get('taxonomy_override_selector', FALSE)) { $parent = array_keys(taxonomy_get_parents($term->tid)); - $children = taxonomy_get_tree($vocabulary->vid, $term->tid); + $children = taxonomy_get_entries($vocabulary->vid, $term->tid); // A term can't be the child of itself, nor of its children. foreach ($children as $child) { @@ -735,7 +735,7 @@ function taxonomy_form_term($form, &$for } $exclude[] = $term->tid; - $tree = taxonomy_get_tree($vocabulary->vid); + $tree = taxonomy_get_entries($vocabulary->vid); $options = array('<' . t('root') . '>'); if (empty($parent)) { $parent = array(0); Index: modules/taxonomy/taxonomy.module =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v retrieving revision 1.603 diff -u -p -r1.603 taxonomy.module --- modules/taxonomy/taxonomy.module 22 Aug 2010 15:45:03 -0000 1.603 +++ modules/taxonomy/taxonomy.module 3 Sep 2010 15:03:44 -0000 @@ -448,7 +448,7 @@ function taxonomy_vocabulary_delete($vid * An array of the term structure that was updated. */ function taxonomy_check_vocabulary_hierarchy($vocabulary, $changed_term) { - $tree = taxonomy_get_tree($vocabulary->vid); + $tree = taxonomy_get_entries($vocabulary->vid); $hierarchy = 0; foreach ($tree as $term) { // Update the changed term with the new parent value before comparison. @@ -680,6 +680,8 @@ function taxonomy_terms_static_reset() { drupal_static_reset('taxonomy_get_tree'); drupal_static_reset('taxonomy_get_tree:parents'); drupal_static_reset('taxonomy_get_tree:terms'); + drupal_static_reset('taxonomy_get_entries'); + drupal_static_reset('taxonomy_get_entries:parents'); drupal_static_reset('taxonomy_get_parents'); drupal_static_reset('taxonomy_get_parents_all'); drupal_static_reset('taxonomy_get_children'); @@ -858,6 +860,99 @@ function taxonomy_get_tree($vid, $parent } /** + * Creates a flat list of term entries, usually used for generating forms or + * overviews of terms. + * + * Note that the entries do not represent full term objects. If full term objects + * are required, use taxonomy_get_tree (needs more processing time). + * + * @param $vid + * Which vocabulary to generate the list for. + * @param $parent + * The term ID under which to load the terms. If 0, return the entire vocabulary. + * @param $max_depth + * The number of hierarchy levels to return. Leave NULL to return all levels. + * + * @return + * An array of all term entries in the vocabulary. Each term entry contains + * the "depth" and "parents" attributes in addition to the data from the + * taxonomy_term_data table. + * Results are statically cached. + */ +function taxonomy_get_entries($vid, $parent = 0, $max_depth = NULL) { + $children = &drupal_static(__FUNCTION__, array()); + $parents = &drupal_static(__FUNCTION__ . ':parents', array()); + $terms = array(); + + // We cache terms, so it's not CPU-intensive to call get_entries() on a term + // and its children, too. + if (!isset($children[$vid])) { + $children[$vid] = array(); + $parents[$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('weight') + ->orderBy('name') + ->execute(); + + foreach ($result as $term) { + $children[$vid][$term->parent][] = $term; + $parents[$vid][$term->tid][] = $term->parent; + } + } + + $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth; + $process_parents = array(); + $process_parents[] = $parent; + while (count($process_parents)) { + $parent = array_pop($process_parents); + $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; + } + $child->depth = $depth; + unset($child->parent); + $child->parents = $parents[$vid][$child->tid]; + $terms[] = $child; + if (!empty($children[$vid][$child->tid])) { + $has_children = TRUE; + + // We have to continue with the current parent later + // and use $child for the next iteration. + $process_parents[] = $parent; + $process_parents[] = $child->tid; + + // Reset pointers for child lists because we step in there more often with multi parents. + reset($children[$vid][$child->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 $terms; +} + +/** * Try to map a string to an existing term, as for glossary use. * * Provides a case-insensitive and trimmed mapping, to maximize the @@ -1275,7 +1370,7 @@ function taxonomy_field_formatter_view($ function taxonomy_allowed_values($field) { $options = array(); foreach ($field['settings']['allowed_values'] as $tree) { - $terms = taxonomy_get_tree($tree['vid'], $tree['parent']); + $terms = taxonomy_get_entries($tree['vid'], $tree['parent']); if ($terms) { foreach ($terms as $term) { $options[$term->tid] = str_repeat('-', $term->depth) . $term->name;