How do I group options in optgroup for select lists with forms api?

Comments

Budrick’s picture

// ..._form_alter...

$term_list = taxonomy_form($vid);   
    $options = array();    
    $tree=taxonomy_get_tree($vid);
    $top_parents=array();    
    foreach($tree as $id => $term){
      if($term->parents[0] == 0) $top_parents[]=$term->tid;
    }
    
    $curr_group='';
    foreach( $term_list["#options"] as $key => $text){
      if( in_array($key, $top_parents) ){
        $options[$text]=array();
        $curr_group=$text;
      }else{
        $options[$curr_group][$key]=$text;
      }
    }    
    $term_list['#options']=$options;
    $form['taxonomy'] = $term_list;
Budrick’s picture

Seems that "option" structure changed in version 5.
So, the code becomes a little more complex:

function module_form_alter(){
   //......

    $term_list = taxonomy_form($vid);
    $term_list['#weight'] = 1;
    $term_list['#title'] = "Title";
    $term_list['#description'] = "Descr";
    $term_list['#attributes'] = array('size' => '30');
    $term_list['#required']=1;
    $options = array();    
    $tree=taxonomy_get_tree($vid);
    $mains=array();    
    foreach($tree as $id => $term){
      if($term->parents[0] == 0) $mains[]=$term->tid;
    }    
    $curr_group='';    
    foreach( $term_list["#options"] as $id => $option){
      $opt = _get_option($option); $key=$opt[0]; $text=$opt[1]; 
      if( in_array($key, $mains) ){
        $curr_group=$text;
        $options[$curr_group]=array();        
      }else{
        $options[$curr_group][]=$option;
      }
    }
    $term_list['#options']=$options;
    return $term_list;
}

function _get_option($option){  
  foreach($option->option as $key=>$value){
    if(is_numeric($key)) return array($key, $value);
  }
}
Laurentvw’s picture

Sorry for my ignorance, but I do not understand forms API. I've looked at documentation but still don't get it. Can someone simply tell me in what file to insert this snippet of code? (I'm using Drupal 5.2 btw)

Also, just to make sure, if I understand it well, this code allows you to put parent categories in < optgroup > ?

Thanks.

Laurentvw’s picture

Anyone? I figured that I have to create my own module, with this code included in it.... I don't have any experience with creating modules though. Anyone willing to help? Thanks.

lias’s picture

I think you create a file called optgroup.module with the code posted above and another file called optgroup.info with :

; $Id: optgroup.info,v 1.0 
name = Optgroup
description = "your description"

; version
version = "5.x"

khanz’s picture

Does it works for Drupal 6.12 .... ??

------------
Volvo, Video, Velcro. (I came, I saw, I stuck around.)

Jonah Ellison’s picture

if (isset($form['taxonomy'][$vid])) {
  $tree = taxonomy_get_tree($vid);
  $parents = array();
  $options[''] = $form['taxonomy'][$vid]['#options'][''];
  foreach ($tree as $term) {
    $obj = new stdClass();
    $obj->option[$term->tid] = $term->name;
    
    if($ptid = $term->parents[0]) {
      $options[$parents[$ptid]][] = $obj;
    } else {
      $parents[$term->tid] = $term->name;
      $options[$term->name] = array();
    }
    
  }
  $form['taxonomy'][$vid]['#options'] = $options;
}
Max K.’s picture

Optgroups are simple in d6...

$form['item'] = array(
  '#type' => 'select',
  '#options' => array(
    'My Opt Group' => array(
      'value' => t('Option'),
      'value2' => t('Option Two')
    ),
    'My Other Opt Group' => array(
      'value3' => t('Option Three'),
      'value4' => t('Option Four.  Heck yeah!')
    )
  )
);
rimu’s picture

Yep. Working example for inside modulename_form_alter:

  if (isset($form['taxonomy'][$vid])) {
      $tree = taxonomy_get_tree($vid);
      $parent = "";
      $options = array();
      foreach ($tree as $term) {
          if($term->depth == 0){
              $parent = $term->name;
          }else{
              if($term->depth > 1){
                $options[$parent][$term->tid] = _modulename_spaces($term->depth, '-') . ' ' . $term->name;
              }else{
                $options[$parent][$term->tid] = $term->name;    
              }
              
          }
        
      }
      $form['taxonomy'][$vid]['#options'] = $options;
      
    }

function _modulename_spaces($number, $char = ' '){
    $output = '';
    for($i = 0; $i < $number; $i++){
        $output .= $char;
    }
    return $output;
}

replace modulename with whatever your module is called.

DrupalGideon’s picture

Rather than the _modulename_spaces function, why not just use

options[$parent][$term->tid] = str_repeat('-', $term->depth) . ' ' . $term->name;

Formerly SkidNCrashwell. Changed my username to reflect my Twitter handle.

Tengu’s picture

What is ptid in your code?

Rob Carriere’s picture

By the looks of it, the taxonomy id of the parent of the current taxonomy item. This has nothing to do with actually using optgroups, the example is creating optgroups from the structure of a taxonomy, so that drags in a lot of taxonomy stuff.

oknate’s picture

This only seems to work with select lists, not with radios. Basically, you create a nested array. This is example is hard-coded. You could abstract it if you needed it to be portable.


		$defaultTax = $form['taxonomy'][7]['#default_value'][0];
		unset($form['taxonomy'][7]);
		
		$vocabOptionsArray = array();
		$query = db_query("SELECT th.tid, td.description FROM {term_hierarchy} th JOIN {term_data} td ON (th.tid = td.tid) WHERE th.parent = '%d' ORDER BY td.weight",475);
		while($row = db_fetch_object($query)) {
			$vocabOptionsArray['Commercial'][$row->tid] = $row->description;
		}
		
		$query = db_query("SELECT th.tid, td.description FROM {term_hierarchy} th JOIN {term_data} td ON (th.tid = td.tid) WHERE th.parent = '%d' ORDER BY td.weight",476);
		while($row = db_fetch_object($query)) {
			$vocabOptionsArray['Narration'][$row->tid] = $row->description;
		}
		

		
	
		

		
		
		$form['taxonomy'][7] = array(
		   '#type' => 'select',
		   '#title' => t('Step 2 - Select Script Category'),
		   '#default_value' => $defaultTax,
		   '#options' => $vocabOptionsArray,
		   '#required' => 1,
		
        );


this code will feed the '#options' parameter an array that looks like this:


Array
(
    [Commercial] => Array
        (
            [136] => Business
            [454] => Food & Beverage
            [455] => Health & Beauty Aids
            [456] => Kids
            [457] => Retail
            [458] => Transportation
            [473] => Travel & Entertainment
            [472] => Character
            [459] => Dialogue
            [460] => Promos, Trailers, Imaging
            [461] => Public Service Announcement
        )

    [Narration] => Array
        (
            [462] => Animation, Cartoon, Videogame, Toys
            [463] => Audiobook
            [464] => Biography
            [465] => Corporate, Industrial
            [466] => Documentary
            [467] => eLearning, Training
            [468] => Inspiration, Relaxation, Meditation
            [469] => Medical, Pharmaceutical
            [470] => Promotional
            [474] => Self-Guided Tours
            [471] => Telephony
        )

)


jessehs’s picture

Here's a form_alter function to accomplish this in a two-tier taxonomy structure. It outputs an array like the previous D6 example.

<?php
/**
 * Implements hook_form_alter().
 */
function mymodule_form_alter(&$form, &$form_state, $form_id) {
  if ($form['#form_id'] == 'my_nodetype_node_form') {    
    $top_level = taxonomy_get_tree($vid = 1, $parent = 0, $max_depth = 1);
    foreach ($top_level as $id => $term) {
      $options[$term->name] = array();
      $children = taxonomy_get_tree(1, $term->tid);
      foreach ($children as $child) {
        $options[$term->name][$child->tid] = $child->name;
      }
    }
    $form['field_taxonomy_name_terms']['und']['#options'] = $options;
  }
}
?>
phreestilr’s picture

Awesome, thanks!

Gik000’s picture

$vocabulary =  taxonomy_vocabulary_machine_name_load('MY-VOCABULARY-NAME');
  $vid = $vocabulary->vid;
  $top_level = taxonomy_get_tree($vid, $parent = 0, $max_depth = Null);
  foreach ($top_level as $id => $term) {
    $tid = $term->tid;
    $children = taxonomy_get_children($tid);
    $parents = taxonomy_get_parents($tid);
    if(!empty($children)){
      //PARENT
      $options[$term->name] = array();
      foreach ($children as $child) {
        $options[$term->name][$child->tid] = $child->name;
      }
    } elseif(empty($parents)) {
      //PARENT WITH NO CHILDREN - LONELY PARENT
      $options[$term->name] = array();
    }
  }
  $form['FIELD-MACHINE-NAME-WITH-OUR-TAXONOMY'][LANGUAGE_NONE]['#options'] = $options;
Anonymous’s picture

Gik, how would you implement your better version? Is it contained within hook_form_alter as the prior block of code??

martin_klima’s picture

You can implement it like this:


function HOOK_form_alter(&$form, $form_state, $form_id) {
...
   $form['YOUR_TERM_REFERENCE_FIELD'][LANGUAGE_NONE]['#options'] = get_optgroup_by_taxonomy('YOUR_TAXONOMY_MACHINE_NAME');
...
}

/**
 * Generate structured (optgrouped) options from taxonomy.
 * Use output as Form API #options array.
 * @param string $taxonomy_name
 * @return array
 */
function get_optgroup_by_taxonomy($taxonomy_name) {
  $options = array();
  $vocabulary = taxonomy_vocabulary_machine_name_load($taxonomy_name);
  $vid = $vocabulary->vid;
  $top_level = taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL);
  foreach ($top_level as $id => $term) {
    $tid = $term->tid;
    $children = taxonomy_get_children($tid);
    $parents = taxonomy_get_parents($tid);
    if (!empty($children)) {
      //PARENT
      $options[$term->name] = array();
      foreach ($children as $child) {
        $options[$term->name][$child->tid] = $child->name;
      }
    }
    elseif (empty($parents)) {
      //PARENT WITH NO CHILDREN - LONELY PARENT
      $options[$term->name] = array();
    }
  }
  return $options;
}

@Gik000 Thank you for share.

bora-89’s picture

Thank you, that is what I was looking for!

jimurl’s picture

This sample code worked great for my purpose as well, Thanks.

I had to call it from _after_build#. I think this is because some other modules ( hierarchical select, possibly, in my case ) are also rewriting the #options array.

HOOK_form_alter(&$form, &$form_state, $form_id) {
...
   $form['#after_build'][] = taxonomy_term_optgroups_after_build';
...
}

function taxonomy_term_optgroups_after_build(&$form, &$form_state){
  $form['YOUR_TERM_REFERENCE_FIELD'][LANGUAGE_NONE]['#options'] =    get_optgroup_by_taxonomy('YOUR_TAXONOMY_MACHINE_NAME');

return $form;
}

And that get_optgroup_by_taxonomy function is identical to the previous example.