#tree and #parents

Summary

The Form API compresses two structures into one, one is the rendering structure defined by the keys of the form array. The other is the data structure which is defined by #parents from which #name (HTML element name) and #id are computed. There is an automated feature which creates #parents from the array keys - it is controlled by #tree and described below.

How it works

All forms are a tree, for example:

           (root)
             |
       Foo  / \
           /   \
          |     |
   Bar   /     /\
        /     /  \
       |
Baz  /
     /

In code, this is written:

<?php
$form
['foo']['bar']['baz']
?>

As long as the #tree attribute is TRUE at any point in the tree, the form element is aware that it is in a tree, and traverses the tree towards the root (from baz, to bar, to foo). Along the way, the names of the modules passed are stored in #parents. #parents is used to create the name/ID of the form element itself.

So, if:

<?php
$form
['foo']['#tree'] == TRUE
?>

and
<?php
$form
['foo']['bar']['#tree'] == TRUE
?>

and
<?php
$form
['foo']['bar']['baz']['#tree'] == TRUE
?>

Then #parents for baz will be array('foo', 'bar', 'baz') and the name of the element in the HTML will be edit['foo']['bar']['baz'].

If, on the other hand,

<?php
$form
['foo']['bar']['baz']['#tree'] == FALSE
?>

Then #parents will only be array('baz') and the name of the element in the HTML edit['baz'].

Cascading of #tree

There are shortcuts to traversing the full tree each time. If you set #tree = TRUE at a closer point to the root of the tree, as in:

<?php
$form
['foo']['#tree'] = TRUE
?>

and you have not specifically set #tree anywhere else, then it will cascade and make all of the sub-elements' #tree = TRUE. This is very useful because otherwise you would need to write #tree = TRUE for each element in the tree.

The FAPI code that deals with #tree and #parents

The following is the section of code from which deals with #tree and #parents<?code>, taken from <code>_form_builder():

<?php
 
foreach (element_children($form) as $key) {
   
// Don't squash an existing tree value.
   
if (!isset($form[$key]['#tree'])) {
     
$form[$key]['#tree'] = $form['#tree'];
    }

   
// Don't squash existing parents value.
   
if (!isset($form[$key]['#parents'])) {
     
// Check to see if a tree of child elements is present. If so,
      // continue down the tree if required.
     
$form[$key]['#parents'] = $form[$key]['#tree'] && $form['#tree'] ?
       
array_merge($form['#parents'], array($key)) : array($key);
    }
?>

Note that the code is not the same to the explanation above for performance reasons. Intead, each element walking towards to the root as long as #tree is TRUE, we pass #parents down as long as #tree is TRUE.

Common use of #tree

A common use of #tree is fieldsets. Another example is the checkboxes element type where #tree is set to TRUE internally before expanding to multiple checkbox elements.

Common use of #parents

You can set #parents manually, but the need for this is rare. More common is to read #parents to determine where in the form tree the current element is. Setting #parents does not affect the rendering of the form, that's decided by the indexes. However, setting #parents does affect placement $form_values as can be seen from filter_form().

An example with insert

When we set fieldset value to TRUE we create the form:

<?php
$form
['colors'] = array(
'#type' => 'fieldset',
'#title' => t('Choose a color'),
'#collapsible' => FALSE,
'#tree' => TRUE,
);
$form['colors']['green'] = array(
'#type' => 'checkbox',
'#title' => t('Green'),
'#default_value' => $node->green,
'#required' => FALSE,
);
$form['colors']['blue'] = array(
'#type' => 'checkbox',
'#title' => t('Blue'),
'#default_value' => $node->blue,
'#required' => FALSE,
);
?>

and this is how they are inserted or updated in a db_query:

<?php
function example_insert($node){
 
db_query("INSERT INTO {example} (nid, question, green, blue) VALUES (%d,'%s', %d, %d)", $node->nid, $node->title, $node->colors['green'], $node->colors['blue']);
}
?>

 
 

Drupal is a registered trademark of Dries Buytaert.