background image

Content tagged with: taxonomy_get_tree

Eric's picture

For some time now I've wanted to write a blog entry about using AHAH to create dynamically generated form elements. After a recent conversation at work regarding usability, I now had a real world example to create: how to use tiered taxonomy to dynamically generate a form. This code snippet will show you how to create a form that creates child select dropdowns based on the parent taxonomy term the user selects.

First I established a multi-tier taxonomy called "AHAH":

For this example I created a menu callback to display my initial form:

<?php
function helper_menu() {
 
 
$items = array();
 
 
$items['ahah-form'] = array(
   
'title' => 'AHAH Form',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('_helper_callback_ahah_form'),
   
'type' => MENU_CALLBACK,
   
'access callback' => 'user_access',
   
'access arguments' => array('access content'),
  );
 
  return
$items;
 
}
?>

I then defined the page callback to show the initial form:

<?php
function _helper_callback_ahah_form() {

 
// define an array to contain form elements
 
$form = array();
 
 
// define the top level vid
 
$vid = 2;
 
 
// fetch a tree of taxonomy elements
 
$tree = taxonomy_get_tree($vid, 0, -1, 1);
 
 
// loop though taxonomy and collect elements
 
$options = array();
  foreach (
$tree as $key => $value) {
   
$options[$value->tid] = $value->name;
  }
 
 
// create the first select dropdown input
 
$form['select_1'] = array(
   
'#type' => 'select',
   
'#options' => $options,
   
'#title' => t('Select 1'),
   
'#size' => 5,
   
'#multiple' => false,
   
'#ahah' => array(
     
'event' => 'change',
     
'path' => 'ahah-form-callback',
     
'wrapper' => 'wrapper-1',
     
'method' => 'replace',
    ),
  );
 
 
// pass the top level vid in the form
 
$form['ahah_vid'] = array(
   
'#type' => 'hidden',
   
'#value' => $vid,
  );
 
 
// create an empty form element to contain the second taxonomy dropdown
 
$form['wrapper_1'] = array(
   
'#prefix' => '<div id="wrapper-1">',
   
'#suffix' => '</div>',
   
'#value' => '&nbsp;',
  );
 
 
// add a form submit button
 
$form['submit'] = array(
   
'#value' => 'Submit',
   
'#type' => 'submit'
 
);
 
  return
$form;
 
}
?>

The above form callback produces the following:

Next, I defined a callback to handle the AHAH page request:

<?php
// new menu item:
function helper_menu() {
 
 
// ...
 
 
$items['ahah-form-callback'] = array(
   
'title' => 'AHAH Form Callback',
   
'page callback' => '_helper_callback_ahah_form_callback',
   
'type' => MENU_CALLBACK,
   
'access callback' => 'user_access',
   
'access arguments' => array('access content'),
  );
 
 
// ...

 
return $items;
 
}

// and, here's the AHAH callback used to create the new form elements:
function _helper_callback_ahah_form_callback() {
 
 
// define a string variable to contain callback output
 
$output = "";
 
 
// pull the top level vid from the $_POST data
 
$vid = $_POST['ahah_vid'];
 
 
// pull the selected dropdown from the $_PODT data
 
$parentVid = $_POST['select_1'];
 
 
// loop through the taxonomy tree and fetch child taxonomies
 
$options = array();
 
$tree = taxonomy_get_tree($vid, $parentVid, -1, 1);  
  foreach (
$tree as $key => $value) {
   
$options[$value->tid] = $value->name;
  }
 
 
// define the second tier select dropdown element
 
$form['select_2'] = array(
   
'#type' => 'select',
   
'#options' => $options,
   
'#title' => t('Select 2'),
   
'#size' => 5,
   
'#multiple' => false,
  );
 
 
// rebuild form object and output new form elements
 
$output .= ahah_render($form, 'select_2');
 
 
// render form output as JSON
 
print drupal_to_js(array('data' => $output, 'status' => true));
 
 
// exit to avoid rendering the theme layer
 
exit();
 
}

// Lastly, here's a help function pulled from Nick Lewis's blog to alter the form
// see: http://www.nicklewis.org/node/967
// NOTE: based on poll module, see: poll_choice_js() function in poll.module
function ahah_render($fields, $name) {
 
$form_state = array('submitted' => FALSE);
 
$form_build_id = $_POST['form_build_id'];
 
// Add the new element to the stored form. Without adding the element to the
  // form, Drupal is not aware of this new elements existence and will not
  // process it. We retreive the cached form, add the element, and resave.
 
$form = form_get_cache($form_build_id, $form_state);
 
$form[$name] = $fields;
 
form_set_cache($form_build_id, $form, $form_state);
 
$form += array(
   
'#post' => $_POST,
   
'#programmed' => FALSE,
  );
 
// Rebuild the form.
 
$form = form_builder($_POST['form_id'], $form, $form_state);

 
// Render the new output.
 
$new_form = $form[$name];
  return
drupal_render($new_form);
}
?>

The above code allows the user to select an option from the top level tier of taxonomy and the AHAH callback will generate the a select dropdown of the child taxonomies as shown below:

On form submission, you'll see that the options the user selected as stored in $form_state['values']['select_1'] and $form_state['values']['select_2']

Eric's picture

Taxonomy is a great way of categorizing your content, and in this case we'll take it a step further to organize your content and create a dynamic layout. In a recent situation, we created a taxonomy category for a given node type and assigned 4 terms to the category. The node type contained an image field and a description. We wanted to show a view all the node images, but categorized by taxonomy term. I thought the best way to accomplish this was to get a list of all the taxonomy terms, loop through them, pass the term ID as an argument to a view, and output each view's html dynamically. In the following code snippet, I created a module that generates a block [see below]

When creating your view, make sure you add an argument for "Taxonomy: Term ID". In my example, I selected "Use empty text" for the default argument option to prevent taxonomy terms from being shown if they do not have any matching nodes.

<?php
function MYMODULE_block($op='list', $delta=0) {
 
$block = array();
  switch (
$op) {
    case
'list':
     
$block[0]['info'] = t('MYBLOCKTITLE');
      return
$block;
      break;
    case
'view':
     
$block['subject'] = NULL;
     
$block['content'] = NULL;
     
$block_content = "";
     
     
// define vocab name
     
$vocabName = 'MYCATEGORYNAME';

     
// define view name
     
$viewName = 'MYVIEWNAME';

     
// define node type
     
$nodeType = 'MYNODETYPE';

     
// get vocabularies for this node
     
$vocabs = taxonomy_get_vocabularies($nodeType));     
     
     
// ensure vocabs exist
     
if (count($vocabs)==0 || !$vocabs) return $block;

     
// loop through vocabs and look for matching name
     
foreach ($vocabs as $k => $v) {
        if (
$v->name == $vocabName) {
         
$vocabID = $v->vid;
          break;
        }
      }

     
// ensure vocabID exists
     
if (!$vocabID) return $block;

     
// get vocab term tree
     
$tree = taxonomy_get_tree($vocabID);

     
// loop through tree and collect term IDs
     
$termIDs = array();
      foreach (
$tree as $k => $v) {
       
$termIDs[$v->tid] = $v->name;
      }

     
// loop through term IDs and create views html
     
foreach ($termIDs as $k => $v) {

       
// get view object
       
$view = views_get_view($viewName); 

       
// ensure result is an object
       
if (!is_object($view)) continue;

       
// create view html
       
$viewHtml = views_build_view('block', $view, array($k), FALSE, $view->nodes_per_block);

        if (
strlen($viewHtml)) {
         
// show the taxonomy term name in an <h3>
         
$block_content .= "<h3>$v</h3>";

         
// add the view html to the block
         
$block_content .= $viewHtml;
        }

      }

     
$block['content'] = $block_content;
      return
$block;
      break;
  }
 
}
?>

The result will resemble the following structure...

Taxonomy Term 1
[node] [node] [node]

Taxonomy Term 2
[node] [node] [node]

Taxonomy Term 3
[node] [node] [node]