5

Due to some complex project, I would like to organize navigation between taxonomy terms programmatically, with generation of custom links.

Example of navigation I need in vocabulary "fruits" with following terms, situated in same order in taxonomy management admin module :

  • apple -> has next link to apricot
  • apricot -> has previous link to apple, has next link to banana
  • banana -> has previous link to apricot, has next link to cherry
  • cherry -> has previous link to banana

The question, is : what is the most convenient method to get the id of previous and next term, in the same order as they organized in vocabulary ?

It's seems what in current taxonomy API of Drupal 7 there is no specific functions to do it in simple way.

2
1

We will make a view, load the term from contextual argument, get its weight into $static variable, and check on each row if row's term's weight is higher than our arg's one.

  1. Create a view of terms, order them by weight.
  2. Display only 1 item.
  3. Add and hide taxonomy term: weight field.
  4. Add a contextual filter : Taxonomy term: Term ID
  5. Add a PhP filter and :

Setup code :

$static = taxonomy_term_load($view->args[0]);
$static = intval($static->weight);

Filter code :

if(intval($data->taxonomy_term_data_weight) < $static) {return TRUE;}

If you're sure that your terms have following weight, you can use that filter code :

if(intval($data->taxonomy_term_data_weight) == intval($static - 1) || intval($data->taxonomy_term_data_weight) == intval($static + 1) ) {return TRUE;}

to get the next and following terms. But careful with 0 and last values... :)

And of course, in that case, you have to display all items in the view.

1
  • Nice Idea but we have a policy against keeping code in the database
    – frazras
    Jan 5 '13 at 4:56
1

You can use taxonomy_get_tree() to do this.

function get_previous_term($vocab, $term) {
  $tree = taxonomy_get_tree($vocab->vid);
  foreach ($tree as $index => $t) {
    if ($t->tid == $term->tid) {
      if (isset($tree[$index-1])) {
        return $tree[$index-1]->tid;
      }
      // $term is the first term in the list.
      return FALSE;
    }
  }
  // not found!
  return NULL;
}

function get_next_term($vocab, $term) {
  $tree = taxonomy_get_tree($vocab->vid);
  foreach ($tree as $index => $t) {
    if ($t->tid == $term->tid) {
      if (isset($tree[$index+1])) {
        return $tree[$index+1]->tid;
      }
      // $term is the last term in the list.
      return FALSE;
    }
  }
  // not found!
  return NULL;
}

I tested this on my localhost and it seems to be working.

$vocab = taxonomy_vocabulary_machine_name_load('tags');
$term =  taxonomy_term_load(2);
$prev = get_previous_term($vocab, $term);
$next = get_next_term($vocab, $term);
dsm("Prev " . $prev);
dsm("Next " . $next);

This code only works for flat vocabularies. If the terms in the vocabulary have hierarchy, you have to take depth and parents of $t into account when trying to find the previous/next term in get_next_term() and get_previous_term() functions.

1

As of my view, if you are doing this via some custom module then we can adopt the API taxonomy_get_tree()

Now only using that the problem won't be solved. Now, this API gives an associative array of the terms for a particular Vocabulary. So for that array use a foreach loop. In that loop check for the first element and for the last element. This will give you the first and last page for the pager. If other elements are not of the first or last then use the iterator's count to make the link. Its better to use a hook_theme() implementation and use of theme('list_item') as well. But if you are not using custom module then the idea with Views will work for you. But again, using views_php module is not supposed as a good practice. Also for this block caching will not work I think.

1
+50

Here's another solution using a simple lightweight db_select query on taxonomy_term_data.

It provides two functions, one to get the next term, and one to get the previous.

The idea is to select the next ( or previous ) term in the same vocabulary with a weight greater ( or less than ) than current term, ordered by weight and limited to one result. A static cache is used so query only performed once per page load.

/**
 * Return next term id by weight
 * @param $term taxonomy term object
 * @return int term id or FALSE if none exists
 */
function EXAMPLE_next_term($term) {

  // Use static cache
  $next = &drupal_static(__FUNCTION__ . '_' . $term->tid, FALSE);

  if(!$next){
    $next = db_select('taxonomy_term_data', 'td')
      ->fields('td', array('tid'))
      ->condition('weight', $term->weight, '>')
      ->condition('vid', $term->vid)
      ->orderBy('weight')
      ->range(0,1)
      ->execute()
      ->fetchField();
  } 

  return $next;

}

/**
 * Return previous term id by weight
 * @param $term taxonomy term object
 * @return int term id or FALSE if none exists
 */
function EXAMPLE_previous_term($term) {

  // Use static cache
  $previous = &drupal_static(__FUNCTION__ . '_' . $term->tid, FALSE);

  if(!$previous){
  $previous = db_select('taxonomy_term_data', 'td')
    ->fields('td', array('tid'))
    ->condition('weight', $term->weight, '<')
    ->condition('vid', $term->vid)
    ->orderBy('weight')
    ->range(0,1)
    ->execute()
    ->fetchField();
  }

  return $previous;

}

Example usage:

if($tid = EXAMPLE_next_term($term)){
  $next_term = taxonomy_term_load($tid); // Load full term
  $link = l($next_term->name, 'taxonomy/term/'.$next_term->tid);
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.