Four Kitchens
Insights

Option groups in Drupal forms

3 Min. ReadDevelopment

One really useful HTML element that doesn’t seem to get much love is optgroup. It allows you to group the items of a select list in a way that may be more meaningful (and more readable) to your users than a long, uninterrupted list. The option group labels themselves can’t be selected, so there’s no need for back end logic to filter them out of your form data.

Drupal’s Form API provides option groups, but it isn’t immediately obvious how to use them. A quick check of the select type and #options attribute in the Form API reference doesn’t provide any clues about option groups, so it’s necessary to dive into Drupal’s source code.

The content management category filter is generated in node_filters(), and if you follow the rabbit hole, you’ll find that the options are generated by taxonomy_form_all(). What this function returns is a nested array that maps vocabulary names to an array of term IDs and term names.

So how do we make use of this? The select element in the figure above could be expressed like this in HTML:

<select id="edit-category" name="category">
  <optgroup label="Image Galleries">
    <option value="1">Book Covers</option>
    <option value="2">Movie Posters</option>
    <option value="3">Illustrations</option>
  </optgroup>
  <optgroup label="Authors">
    <option value="4">Arthur C. Clark</option>
    <option value="5">Frank Herbert</option>
    <option value="9">Robert Heinlein</option>
  </optgroup>
  <optgroup label="Genres">
    <option value="10">Alien Invasion</option>
    <option value="14">Space Wars</option>
  </optgroup>
</select>

Typically for a select item, we set the #options attribute to an array of scalar types (numbers, strings, and booleans). Each element of the array corresponds to an HTML option whose value attribute is the element’s index (or key) and whose value becomes the text inside the option element.

To create option groups, the #options attribute is given an array of arrays. The keys of the outer array become the label attributes for optgroup elements. Each element of the inner array is then used to create an option element contained within that optgroup.

To get that result using the Form API, we would create an array for the #options attribute like this:

$options = array(
  // — First option group
  'Image Galleries' => array(
    1 => 'Book Covers',
    2 => 'Movie Posters',
    3 => 'Illustrations',
    ),
  // — Second option group
  'Authors' => array(
    4 => 'Arthur C. Clark',
    5 => 'Frank Herbert',
    // …
    9 => 'Robert Heinlein',
    ),
  // — Third option group
  'Genres' => array(
    10 => 'Alien Invasion',
    // …
    15 => 'Space Wars',
    ),
  );

$form['category'] = array(
  '#title' => t('Category'),
  '#type' => 'select',
  '#options' => $options,
  );

One of the benefits of the option group is that the labels can’t be selected. This means you don’t have to insert dummy grouping values in the select list that you then have to filter out in your form submit handler. And since the values for the options in a select element don’t have to be unique, you can place the same option in multiple groups if you need to.

Now go forth and be groupies! :-p