Community Documentation

#tree and #parents

Last updated November 15, 2011. Created by mlsamuelson on February 9, 2006.
Edited by kay_v, fgm, xenophyle, Matt V.. Log in to edit this page.

This page had been archived.

A more recent/complete treatment of this information can be found at Form API Quickstart Guide.

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 that 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, taken from _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. Instead, 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 checkbox 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 of $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']);
}
?>

Comments

Make sure $form['foo']['#type'] is null

After some experimentation, it looks like if $form['foo']['#type'] is set to anything, this whole '#tree' thing seems to auto fail. This structure is extremely useful for making your $form_values in the _submit stage much easier to work with though.

The solution of course is not to nest form elements (except for fieldsets, which can be nested with their children only it appears).

I'm not sure this is the intended behaviour or not, but I think this page needs to be updated to reflect the reality.

Thank you for the above! I

Thank you for the above! I spent hours trying to figure why my tree structure did not have the same number of array keys on each fieldset. The above fixed my problems.

===
Elvis McNeely (past username: mcneelycorp)
Drupal Snippets: http://www.elvisblogs.org/drupal
My Company: http://www.lafayetteweb.com

About this page

Drupal version
Drupal 4.7.x, Drupal 5.x, Drupal 6.x

Archive

Drupal’s online documentation is © 2000-2012 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.