The Definitive Guide to Drupal 7

Theming Forms with Theme Functions

The decision of whether to use a theme function or a template file is a personal/team preference. If you’re comfortable using PHP, you might be inclined to use theme functions. If not, you’ll probably prefer a template file, which is explained in the next section.

As discussed above, you’ll need a hook_theme() implementation, without a template or path index, as shown in Listing 16–28. After doing this, HOOK_contact_site_form() is an official theme hook that can be overridden like any other theme function. Even though a theme_contact_site_form() function doesn’t exist, you still name it as you would any other theme function override: THEMENAME_contact_site_form().

Listing 16–28. The basic required code for theming a form with a theme function
  1. <?php
  2. /**
  3.  * Implements hook_theme().
  4.  */
  5. function dgd7_theme() {
  6. return array(
  7. 'contact_site_form' => array(
  8. 'render element' => 'form',
  9. ),
  10. );
  11. }
  12. /**
  13.  * Implements theme_forms_contact_site_form().
  14.  */
  15. function dgd7_contact_site_form($variables) {
  16. // Renders all elements of a form.
  17. return drupal_render_children($variables['form']);
  18. }

Using drupal_render_children() Is a Must!

drupal_render_children() takes care of rendering all of the children of the form. This function alone will result in the exact same code Drupal would have provided without your theme function, which makes the function in Listing 16–28 pretty useless by itself, but it’s worth stressing that it’s VERY important to always use drupal_render_children($variables['form']) at the bottom of your function.

Even if you call render() on every element you have added to the form, Drupal will have added some important hidden elements identifying the form and those need to be rendered, too. So calling drupal_render_children($form) at the end of the theme function is mandatory. This won’t re-print $form['foo'] because drupal_render() knows it has printed already. As an added bonus, it will take care of any additional elements added by other modules.

Manipulating Form Elements in Theme Functions

Now that we've gotten that out of the way, let’s make some changes to the markup. Just like any theme function, the code this function returns will be inserted directly into the page markup. Since forms are render elements you need to render them. The code in Listing 16–29, does the following:

  1. Changes the labels of the name and mail elements.
  2. Renders the name and mail elements individually.
  3. Arranges the markup and individually rendered elements in a variable called $output.
  4. Includes drupal_render_children($form) in the $output at the bottom of the theme function.
  5. Finally, it returns the $output.
Listing 16–29. Implements theme_contact_site_form()
  1. <?php
  2. /**
  3.  * Implements theme_contact_site_form().
  4.  */
  5. function dgd7_contact_site_form($variables) {
  6. // Hide the subject field. It's not required.
  7. hide($variables['form']['subject']);
  8.  
  9. // Change the labels of the "name" and "mail" textfields.
  10. $variables['form']['name']['#title'] = t('Name');
  11. $variables['form']['mail']['#title'] = t('E-mail');
  12.  
  13. // Create output any way you want.
  14. $output = '<div class="something">';
  15. $output .= '<p class="note">'. t("We'd love hear from you. Expect to hear back from us in 1-2 business days.") .'</p>';
  16. $output .= render($variables['form']['name']);
  17. $output .= render($variables['form']['mail']);
  18. $output .= '</div>';
  19.  
  20. // Be sure to include a rendered version of the remaining form items.
  21. $output .= drupal_render_children($variables['form']);
  22.  
  23. // Return the output.
  24. return $output;
  25. }

Forms and their contents are render elements, so you can use hide(), show(), and render() functions to manipulate the elements of the form. When using hide() or making changes to the form array inside the theme function, you’ll need to make sure you do so before attempting to render. There are a lot of other things that can be done here. We can’t possibly cover all of them, but here are a few quick examples of what can be done:

  • Adjust the #weight property of an element to change the order in which they print. The following code would cause the message element to print at the top of the form:

    1. $variables['form']['message']['#weight'] = -10;
    2. $variables['form']['message']['#sorted'] = FALSE;
  • Add a description underneath an element by setting the element #description property, like so:

    1. $variables['form']['mail']['#description'] = t("We won't share your e-mail with anyone.");
  • Set the default value of form element, such as checking the "Send yourself a copy" checkbox, by default setting the #checked property to TRUE, like so:

    1. $variables['form']['copy']['#checked'] = TRUE;
  • Unset the #theme_wrappers property to remove the label and wrapper <div> and re-create the markup exactly the way you want it, like so:

    1. unset($variables['form']['mail']['#theme_wrappers']);
  • More advanced changes include making the form display in a table by using the theme_table() function.

  • … and so on!

You are reading content from two chapters on Theme Development from The Definitive Guide to Drupal 7, written by Jacine Luisi and published by Apress on July 19, 2011. All rights reserved.