You are here

HLKD7FOTW - element_get_visible_children()

Submitted by Dave on 17 February 2011 - 4:17pm

Oh hey there. It's been more than a week hasn't it? Well shoot. Here's part two of my (now semi-) weekly series about a 'helpful lesser-known Drupal 7 functions' (HLKD7F Of The Week). I'm sharing fun Drupal 7 tidbits that I've discovered along the way; because even though they are obscure, they can be very helpful as a module or core developer.

Now I present... *drumroll* ...

Given a form or render-able page array element, it will return an array of the keys of the visible children 'contained' inside the element. It knows what sub-elements there are by use of the also-handy element_children() function. Typically you would use this for fieldset and container elements (the latter being new to Drupal 7). The return value can easily be cast into a boolean TRUE/FALSE value for use with '#access' properties.

Real world example

The Override Node Options module (led by timmillwood, and co-maintained by joachim and myself) makes a good example for how to use this function:

 * Implements hook_form_alter().
function override_node_options_form_node_form_alter(&$form, $form_state) {
  // Get a copy of the current node object.
  $node = $form['#node'];

  // Add access to the 'Authoring information' fieldset.
  $form['author']['name']['#access'] = user_access('override ' . $node->type . ' authored by option');
  $form['author']['date']['#access'] = user_access('override ' . $node->type . ' authored on option');
  $form['author']['#access'] |= (bool) element_get_visible_children($form['author']);

By selectively setting access to elements in node form's fieldsets, it would not be good if we hide all of a fieldset's elements, but still display an empty fieldset. That would be confusing. So for each fieldset the module alters, it also checks to see if there are any visible items left. It use the boolean OR operator (|=) on the Authoring information fieldset's access property because node.module's node_form() already defines $form['author']['#access'] = user_access('administer nodes'); and we want to only see the fieldset it if either the original access value is TRUE or the return value from element_get_visible_children() is TRUE.

In this case, here's is how the function gets its results:

Value of $form['author']['name']['#access'] Value of $form['author']['date']['#access'] Result of element_get_visible_children($form['author']) Result of (bool) element_get_visible_children($form['author'])
TRUE TRUE array('name', 'date') TRUE
TRUE FALSE array('name') TRUE
FALSE TRUE array('date') TRUE

Best practices with hiding items

The above code from Override Node Options also highlights some best practices when you want to 'hide' elements on a form or page array. You should always use $form['myitem']['#access'] = FALSE; rather than unset($form['myitem']); as other modules that are altering the same form or page may also be altering that same element. When you use unset() the array no longer exists, so it would cause a PHP notice if a later form alter tried to do something like $form['myitem']['#title'] = t('My changed title');.

Add visibility checking by default for Drupal 8?

Maybe we should add a core patch for Drupal 8 to automatically check element_get_visible_children() before any container elements are rendered? Once a form has been altered it would be silly to output an empty fieldset, and it would be nice to not have this burden placed on every module that alters forms or page output.