#tree and #parents

Last updated on
21 June 2021

Drupal 7 will no longer be supported after January 5, 2025. Learn more and find resources for Drupal 7 sites

Normally when working with submitted data in $form_state['values'] the data is flattened and does not maintain the structure of the $form array used to generate the form. This behavior can be changed using the #tree property.

The #tree property is a boolean (default FALSE) used for collections of form elements. It is used in part along with #parents to determine the name that the <input> element in the HTML form will receive, and therefore also influences how data can be accessed in the $form_state array when a form is submitted.

Consider the following form array:

$form['name'][1]['first'] = [
// text field ...
];

$form['name'][1]['last'] = [
// text field ...
];

$form['name'][2]['first'] = [
// text field ...
];

$form['name'][2]['last'] = [
// text field ...
];

When rendered to HTML this would result in a form with 4 textfields. The names of those elements will vary depending on the use of #tree. By default, the name attribute is taken from the value of the last key, so in effect you'll end up with two input fields with a name attribute of "first" and 2 with a name attribute of last. Furthermore, by default the form API will flatten the form structure when processing submitted values. The above code would result in:

$form_state['values'] = [
  'first' => '', // Value of name field
  'last' => '', // Value of last field
];

Notice there are only 2, and not 4 values in the array? In this case you'll only have access to the submitted values of the last element defined in the $form array with that key.

If however, you set $form['name']['#tree'] = TRUE;, or 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 first, to 1, to name). Along the way, the names of the elements passed are stored in#parents#parentsis used to create the name/ID of the HTML form element itself.

Example:

<input name="first"> vs. <input name="name[1][first]">

With #tree set to TRUE the structure of the $form array is maintained and $form_state['values'] would be as follows:

$form_state['values'] = [
  'name' => [
    1 => ['first' => '', 'last' => ''],
    2 => ['first' => '', 'last' => ''],
  ],
];

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.

Common use of #tree

A common use of #tree is fieldsets. Another example is the checkbox element type where #treeis 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 #parentsto 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().

#parents vs. #array_parents

#array_parents contains the actual parent array keys of an element in a form structure and is useful if you need to locate an item in the $form array.

#parents always contains the parent array keys of an element in the submitted form values and is heavily influenced as we saw above by the use of #tree.

For example:

$form['foo']['bar']['beer'] = [...];

The element 'beer' will always have:

'#array_parents' => ['foo', 'bar', 'beer']

This is heavily influenced by previously defined #parents further up the tree, or, when speaking of tree, it's also affected by whether some element above the element defined #tree, or, whether the element itself defines #tree.

For example:

$form['foo']['bar'] = [...];

will end up in

$form_state['values']['bar'] = '...';

and we'll see

'#parents' => ['bar']

For example:

$form['foo']['bar'] = [...];
$form['foo']['#tree'] = TRUE;

will end up in

$form_state['values']['foo']['bar'] = '...';

and we'll see

'#parents' => ['foo', 'bar']

For example:

$form['foo']['bar'] = [...];
$form['foo']['#parents'] = ['beer'];

will end up in

$form_state['values']['beer']['bar'] = '...';

and we'll see

'#parents' => ['beer', 'bar']

For example:

$form['foo']['bar'] = ['#default_value' => 'bar'];
$form['foo']['#tree'] = TRUE;
$form['foo']['bar']['beer'] = ['#default_value' => 'beer'];
$form['foo']['bar']['beer']['#tree'] = FALSE;

will end up in

$form_state['values']['foo']['bar'] = 'bar';
$form_state['values']['beer'] = 'beer';

and we'll see

'#parents' => ['foo', 'bar']
'#parents' => ['beer']

Help improve this page

Page status: No known problems

You can: