function drupal_render

You are here

7 common.inc drupal_render(&$elements)
5 common.inc drupal_render(&$elements)
6 common.inc drupal_render(&$elements)
8 common.inc drupal_render(&$elements, $is_recursive_call = FALSE)

Renders HTML given a structured array tree.

Recursively iterates over each of the array elements, generating HTML code.

Renderable arrays have two kinds of key/value pairs: properties and children. Properties have keys starting with '#' and their values influence how the array will be rendered. Children are all elements whose keys do not start with a '#'. Their values should be renderable arrays themselves, which will be rendered during the rendering of the parent array. The markup provided by the children is typically inserted into the markup generated by the parent array.

HTML generation for a renderable array, and the treatment of any children, is controlled by two properties containing theme functions, #theme and #theme_wrappers.

#theme is the theme function called first. If it is set and the element has any children, it is the responsibility of the theme function to render these children. For elements that are not allowed to have any children, e.g. buttons or textfields, the theme function can be used to render the element itself. If #theme is not present and the element has children, each child is itself rendered by a call to drupal_render(), and the results are concatenated.

The #theme_wrappers property contains an array of theme functions which will be called, in order, after #theme has run. These can be used to add further markup around the rendered children; e.g., fieldsets add the required markup for a fieldset around their rendered child elements. All wrapper theme functions have to include the element's #children property in their output, as it contains the output of the previous theme functions and the rendered children.

For example, for the form element type, by default only the #theme_wrappers property is set, which adds the form markup around the rendered child elements of the form. This allows you to set the #theme property on a specific form to a custom theme function, giving you complete control over the placement of the form's children while not at all having to deal with the form markup itself.

drupal_render() can optionally cache the rendered output of elements to improve performance. To use drupal_render() caching, set the element's #cache property to an associative array with one or several of the following keys:

  • 'keys': An array of one or more keys that identify the element. If 'keys' is set, the cache ID is created automatically from these keys. See drupal_render_cid_create().
  • 'granularity' (optional): Define the cache granularity using binary combinations of the cache granularity constants, e.g. DRUPAL_CACHE_PER_USER to cache for each user separately or DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to cache separately for each page and role. If not specified the element is cached globally for each theme and language.
  • 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is required. If 'cid' is set, 'keys' and 'granularity' are ignored. Use only if you have special requirements.
  • 'expire': Set to one of the cache lifetime constants.
  • 'bin': Specify a cache bin to cache the element in. Defaults to 'cache'.

This function is usually called from within another function, like drupal_get_form() or a theme function. Elements are sorted internally using uasort(). Since this is expensive, when passing already sorted elements to drupal_render(), for example from a database query, set $elements['#sorted'] = TRUE to avoid sorting them a second time.

drupal_render() flags each element with a '#printed' status to indicate that the element has been rendered, which allows individual elements of a given array to be rendered independently and prevents them from being rendered more than once on subsequent calls to drupal_render() (e.g., as part of a larger array). If the same array or array element is passed more than once to drupal_render(), it simply returns an empty string.

Parameters

array $elements: The structured array describing the data to be rendered.

Return value

string The rendered HTML.

89 calls to drupal_render()
ajax_prepare_response in includes/ajax.inc
Converts the return value of a page callback into an Ajax commands array.
authorize.php in ./authorize.php
Administrative script for running authorized file operations.
bartik_field__taxonomy_term_reference in themes/bartik/template.php
Implements theme_field__field_type().
book_children in modules/book/book.module
Formats the menu links for the child pages of the current page.
book_node_export in modules/book/book.module
Generates printer-friendly HTML for a node.

... See full list

1 string reference to 'drupal_render'
DrupalRenderTestCase::testDrupalRenderChildrenAttached in modules/simpletest/tests/common.test
Test #attached functionality in children elements.

File

includes/common.inc, line 5838
Common functions that many Drupal modules will need to reference.

Code

function drupal_render(&$elements) {
  // Early-return nothing if user does not have access.
  if (empty($elements) || (isset($elements['#access']) && !$elements['#access'])) {
    return '';
  }

  // Do not print elements twice.
  if (!empty($elements['#printed'])) {
    return '';
  }

  // Try to fetch the element's markup from cache and return.
  if (isset($elements['#cache'])) {
    $cached_output = drupal_render_cache_get($elements);
    if ($cached_output !== FALSE) {
      return $cached_output;
    }
  }

  // If #markup is set, ensure #type is set. This allows to specify just #markup
  // on an element without setting #type.
  if (isset($elements['#markup']) && !isset($elements['#type'])) {
    $elements['#type'] = 'markup';
  }

  // If the default values for this element have not been loaded yet, populate
  // them.
  if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) {
    $elements += element_info($elements['#type']);
  }

  // Make any final changes to the element before it is rendered. This means
  // that the $element or the children can be altered or corrected before the
  // element is rendered into the final text.
  if (isset($elements['#pre_render'])) {
    foreach ($elements['#pre_render'] as $function) {
      if (function_exists($function)) {
        $elements = $function($elements);
      }
    }
  }

  // Allow #pre_render to abort rendering.
  if (!empty($elements['#printed'])) {
    return '';
  }

  // Get the children of the element, sorted by weight.
  $children = element_children($elements, TRUE);

  // Initialize this element's #children, unless a #pre_render callback already
  // preset #children.
  if (!isset($elements['#children'])) {
    $elements['#children'] = '';
  }
  // Call the element's #theme function if it is set. Then any children of the
  // element have to be rendered there.
  if (isset($elements['#theme'])) {
    $elements['#children'] = theme($elements['#theme'], $elements);
  }
  // If #theme was not set and the element has children, render them now.
  // This is the same process as drupal_render_children() but is inlined
  // for speed.
  if ($elements['#children'] == '') {
    foreach ($children as $key) {
      $elements['#children'] .= drupal_render($elements[$key]);
    }
  }

  // Let the theme functions in #theme_wrappers add markup around the rendered
  // children.
  if (isset($elements['#theme_wrappers'])) {
    foreach ($elements['#theme_wrappers'] as $theme_wrapper) {
      $elements['#children'] = theme($theme_wrapper, $elements);
    }
  }

  // Filter the outputted content and make any last changes before the
  // content is sent to the browser. The changes are made on $content
  // which allows the output'ed text to be filtered.
  if (isset($elements['#post_render'])) {
    foreach ($elements['#post_render'] as $function) {
      if (function_exists($function)) {
        $elements['#children'] = $function($elements['#children'], $elements);
      }
    }
  }

  // Add any JavaScript state information associated with the element.
  if (!empty($elements['#states'])) {
    drupal_process_states($elements);
  }

  // Add additional libraries, CSS, JavaScript an other custom
  // attached data associated with this element.
  if (!empty($elements['#attached'])) {
    drupal_process_attached($elements);
  }

  $prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
  $suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
  $output = $prefix . $elements['#children'] . $suffix;

  // Cache the processed element if #cache is set.
  if (isset($elements['#cache'])) {
    drupal_render_cache_set($output, $elements);
  }

  $elements['#printed'] = TRUE;
  return $output;
}

Comments

Some more information:

Fixed link for Page render drill down in Drupal 7:
http://sf2010.drupal.org/conference/sessions/page-render-drill-down-drup...

Now just fix the noise he makes after ever sentence.

drupal_render() is not working well with ajax. I'm not sure why or maybe my code is wrong. but when I create a loop with form elements and render is using drupal render then put it again in array so that my table will now have a textfields in rows. when I view source I've noticed that the ajax on class is gone.

<?php
  $options
= array();
  for (
$i = 0; $i < $total_parts; $i++) {
   
/** FOR ITEM CODE **/
   
$form['code'.$i] = array(
     
'#id' => 'code'.$i,
     
'#type' => 'textfield',
     
'#title' => NULL,
     
'#name' => 'code'.$i,
     
'#size' => 10,
    );
   
   
/** FOR DESCRIPTION **/
   
$form['description'.$i] = array(
     
'#type' => 'select',
     
'#title' => NULL,
     
'#id' => 'description'.$i,
     
'#name' => 'description'.$i,
     
'#options' => scheduler_get_parts_per_task($taskID),
     
'#attributes' => array('style' => 'width: 100%',),
    );

   

/** FOR UNITS **/
   
$form['units'.$i] = array(
     
'#id' => 'units'.$i,
     
'#type' => 'textfield',
     
'#title' => NULL,
     
'#name' => 'units'.$i,
     
'#size' => 5,
    );
 
   
/** FOR QTY **/
   
$form['qty'.$i] = array(
     
'#id' => 'qty'.$i,
     
'#type' => 'textfield',
     
'#title' => NULL,
     
'#name' => 'qty'.$i,
     
'#size' => 5,
    );

   

/** FOR PRICE **/
   
$form['price'.$i] = array(
     
'#id' => 'price'.$i,
     
'#type' => 'textfield',
     
'#title' => NULL,
     
'#name' => 'price'.$i,
     
'#size' => 10,
     
'#ajax' => array(
       
'callback' => 'ajax_multifield_callback',
       
'wrapper' => 'subtotal_wrapper-' . $i,
      ),
    );
   
   
/** FOR SUBTOTAL **/
   
$form['total_price'.$i] = array(
     
'#id' => 'subtotal'.$i,
     
'#type' => 'textfield',
     
'#title' => NULL,
     
'#name' => 'subtotal'.$i,
     
'#size' => 10,
     
'#prefix' => '<div id="subtotal_wrapper-' . $i . '">',
     
'#suffix' => '</div>',
    );

    if (!empty(

$form_state['values']['price'.$i])) {
     
$form['total_price'.$i]['#default_value'] = $form_state['values']['price'.$i];
     
$form['total_price'.$i]['#value'] = $form_state['values']['price'.$i];
    }
      
   
$options[$i] = array(
     
drupal_render($form['code'.$i]),
     
drupal_render($form['description'.$i]),
     
drupal_render($form['units'.$i]),
     
drupal_render($form['qty'.$i]),
     
drupal_render($form['price'.$i]),
     
drupal_render($form['total_price'.$i]),
    );
  }
 
 
$header = array(
     
'CODE' => array('data' => t('CODE'), 'width'=> '15%'),
     
'DESCRIPTION' => array('data' => t('DESCRIPTION'),'width'=> '50%'),
     
'UNITS' => array('data' => t('UNITS'), 'width'=> '10%'),
     
'QTY' => array('data' => t('QTY'), 'width'=> '10%'),
     
'PRICE' => array('data' => t('PRICE'), 'width'=> '15%'),
     
'SUBTOTAL' => array('data' => t('SUBTOTAL'), 'width'=> '15%'),
     
  );
   
 
$form['list']['bom_details'] = array(
   
'#theme' => 'table',
   
'#rows' => $options,
   
'#header' => $header,
   
'#empty' => t('You did not add parts to this task.'),
  );
?>

I've come across exactly the same problem. I am trying to place a submit button with an ajax callback inside a table. If i use

$rows[] = array(
$form['my_submit'],
);

Then the form element (button in this case) is drawn outside the table and the ajax callback is working correctly

If instead I use:

$rows[] = array(
drupal_render($form['my_submit']),
);

Then the button is inside the table but the ajax callback is not called at all.

Using firebug I confirmed that "ajax processed" is missing in the second case.
So is something wrong with my approach or drupal_render can't handle #ajax that way?

Similar problem. Drupal render does not place the default_value on an ajax call. Even though the value exists and is correct using drupal_render($form_element) removes the value from the array.

Trying to include an entity via "entity_view" within a div that's replaced during #AJAX callback doesn't work for me. There's a problem with both:

$form['element'] = entity_view('entity_type', array($entity));

and

$form['element'] = array(
   '#markup' => drupal_render(entity_view('entity_type', array($entity))),
);

The problem seems similar to those stated above. Basically AJAX is triggered, but nothing is replaced on the form.

In fact, having the entity_view('type', $entity) in the form function even without including the results in the $form array has similar results. Removing the '#ajax' property makes everything work fine, including just setting a $form element equal to the $entity_view_array and letting the form system render it.

When you do, please create a new comment linking to the issue from here.

@sspiros, @carlmcdade, @darnzen, @yareckon,

I defined the form array in a function and then just returned it, i.e.:

function _my_form_definition($form, &$form_state)
{
  $record = $form_state['build_info']['args'][0];

  $form = array();
  // define the form based on $record that has #ajax keys in form elements
  return $form;
}

function _some_other_function()
{
  ...
  // for example, while generating a table with ajax form elements
  $header = array(...);
  $rows = array();
  foreach($result as $record) // result is from some db query
  {
     $rows[] = array(..., drupal_render(drupal_get_form('_my_form_definition', $record)),...);
  }
  ...
  return theme('table', array('header'=>$header, 'rows'=>$rows));
}

This should respect the ajax functionality. This worked for me. If you need further elaboration, let me know.

I was facing the same problem with ajax forms, whenever I used drupal_render_children($form) the form with ajax worked perfectly but, when I wanted to organize the elements within my custom page I had to use render or drupal_render the problem with this is that when you use drupal_render_children it will print everything, and it seems there is no way to selectively render elements with drupal_render_children. So after some debuggin there is a #printed property for each element of the $form you have, and drupal_render_children($form) won't print it if that property is set to true. Then if you have some form element like:

<?php
$form
['date-from'] = array(
       
'#type' => 'textfield',
       
'#title' => t('From'),
       
'#size' => 15,
       
'#maxlength' => 15,
    );

   

$form['date-to'] = array(
       
'#type' => 'textfield',
       
'#title' => t('To'),
       
'#size' => 15,
       
'#maxlength' => 15,
    );

   

$form['submit'] = array(
       
'#type' => 'submit',
       
'#value' => t('Go'),
       
'#submit' => array('my_form_submit'),
       
'#ajax' => array(
           
'callback' => 'my_form_submit_callback',
           
'wrapper' => 'data',
           
'method' => 'replace',
           
'effect' => 'fade',
        ),
    );

   

$form['link-download'] = array(
       
'#type' => 'link',
       
'#title' => t('Download!'),
       
'#href' => 'download/nojs/'],
       
'#prefix' => '<div id="link-download">',
       
'#suffix' => '</div>',
       
'#ajax' => array(
         
'wrapper' => 'link-download',
         
'method' => 'replace',
         
'effect' => 'slide',
        ),
    );

   

$form['target-download'] = array(
       
'#type' => 'markup',
       
'#prefix' => '<div id="download">',
       
'#suffix' => '</div>'
       
);
?>

in your theme (.tpl.php) file you should do the following:

<?php $form['link-download']['#printed'] = 1;?>
<?php $form['target-download']['#printed'] = 1;?>

then print your submit (ajax) form with drupal_render_children() :

<?php
print drupal_render_children($form);
?>

and then set the previous form elements to false:

<?php $form['link-download']['#printed'] = 0;?>
<?php $form['target-download']['#printed'] = 0;?>
<?php print render($form['link-download']);?>
<?php print render($form['target-download']);?>

In that way your form will submit via ajax correctly.

...and your form theme function never completes, it might be because you are used to the D6 practice of calling drupal_render($form) at the end of a theme_mymodule_example_form() function to render all the parts you haven't yet rendered. Don't forget that in D7 modules should call drupal_render_children() and not drupal_render() in theme_() functions, otherwise they will end up in an infinite loop.

Given that this is such a difficult to diagnose problem and it is a change to a previous item of good practice, I am surprised that the wise folks who decided this would be a fine change did not manage to put anything in place to catch the infinite loop and stop it, by either making this plain and simple backwards-compatible or by stopping and reporting an error. But they did not. I hope that this comment being here will prevent others (and maybe even myself) from hours of searching for an answer in future!

Thank you for this information I got this error while hacking form display into a template via a theme, and render() (alias for drupal_render() ) caused infinite loop, whereas drupal_render_children() managed to correctly print the form().

This is because I changed the theming function through hook_form_alter(), then trying to render the form in the template will call the theming function, which will again load the template, and so on...

In my case, it's logical : once in the template file specified in my custom theme, the form is already "rendered", so just the child elements (the form itself) need to be rendered.

It would be really useful if there were some hooks in the form module_invoke_all('drupal_post_render', $output);

Does the proposed solutions @ http://api.drupal.org/comment/32753#comment-32753 still apply? I am having difficulties rendering my template with autocomplete function

Remember that only variables can be passed by reference. Therefore drupal_render() must be called with a variable. You cannot call it with something like:

<?php
drupal_render
(array(
 
'#theme' => 'links'
  '#links'
=> array(
   
// etc.
 
),
 
'#attributes' => array(
   
// etc.
 
),
));
?>

You will get a white screen of death and a fatal error in your server log.

It is easily solved:

<?php
$links
= array(
 
'#theme' => 'links'
  '#links'
=> array(
   
// etc.
 
),
 
'#attributes' => array(
   
// etc.
 
),
);
drupal_render($links);
?>