How to theme a Drupal form

Key words and phrases: 
Howto theme Drupal forms themeing input forms user_register with bio module
Description & Info: 

Update: This tutorial has been revised significantly to provide a robust solution consistently works.

Define a function named your_theme_name_ or phptemplate_ plus name_of_form($form).

  • Tip: You can find out what the names of forms are by implementing hook_form_alter in a module and adding the code drupal_set_message("Form ID: " . $form_id);, as in the first commented out line in this example.

<?php
function phptemplate_user_register($form) {
   
$variables = array('user' => $user, 'form' => $form);
    return
_phptemplate_callback('user_register', $variables);
}
?>

Then create a .tpl.php file with that name.

In this case, user_register.tpl.php

We can use this PHP snippet:

<?php
drupal_set_message
('< pre >'. var_export($variables,TRUE) .'< /pre >');
?>

This provides us with the variable names (inside the $form variable) that are available to "drupal render" at your own chosen place.

  • Tip: To preserve the functionality of Drupal's catch-all-remaining form elements, you must pass the the form values in from template.php in a broader variables array (as described above). This allows you to use a form variable with form element names as keys, rather than using form names directly. Form names directly will build the form, but without the ability to drupal_render all remaining form elements, and several hidden ones are essential to a functioning Drupal form.

Example of a form theming .tpl.php file:

<div id="regform1">
<fieldset class="group-account-basics collapsible"><legend>Account Basics</legend>

<?php print drupal_render($form['name']); ?>

<?php print drupal_render($form['mail']); ?>

<?php print drupal_render($form['pass']); ?>

<?php print drupal_render($form['field_user_picture']); ?>

<?php print drupal_render($form['field_city']); ?>

</fieldset>
</div>


<div id="regform2">
<fieldset class="group-story collapsible"><legend>My Story</legend>

<?php print drupal_render($form['field_storypic']); ?>

<?php print drupal_render($form['field_user_story_field']); ?>

</fieldset>
</div>

<?php print drupal_render($form['captcha']); ?>

<?php print drupal_render($form['submit']); ?>

<?php
unset($form['field_mostimportantlesson']);
print
drupal_render($form);
?>

Note the last step: we unset any fields we don't want to show, and then print the remainder of the form. If you drupal_render($form) outputs something you don't want or in a place you don't want it, simply unset that field or drupal_render that field specifically where you do want it.

Modifying individual fields

That's great, we can put fields in any order, surround them with any markup we want, and skip ones we don't want. What about changing the output of a given field?

We will only discuss here the simple way, very similar to form_alter, yet powerful enough to almost certainly meet all your needs with the addition of CSS.

In this case, we want to do something special with the age at diagnosis field.

To see what we're doing and make an initial attempt which proved incorrect, we added this to our user_register.tpl.php file in our theme's directory:

<?php
drupal_set_message
('<pre>'.print_r($form['field_ageatdiagnosis'],TRUE).'</pre>');
$form['field_ageatdiagnosis']['#suffix'] = ' '. t('years');
print
drupal_render($form['field_ageatdiagnosis']);
?>

That printed "years" at the end of the whole form, not what we had hoped. We will be able to refine our attempt based on the output of drupal_set_message print_r on the field, below:

Array
(
    [#tree] => 1
    [0] => Array
        (
            [value] => Array
                (
                    [#type] => textfield
                    [#title] => Age at Diagnosis
                    [#default_value] => 
                    [#required] => 0
                    [#description] => 
                    [#size] => 20
                    [#maxlength] => 11
                    [#attributes] => Array
                        (
                            [class] => number
                        )

                    [#field_prefix] => 
                    [#field_suffix] => 
                    [#post] => Array
                        (
                        )

                    [#programmed] => 
                    [#tree] => 1
                    [#parents] => Array
                        (
                            [0] => field_ageatdiagnosis
                            [1] => 0
                            [2] => value
                        )

                    [#weight] => 0
                    [#processed] => 
                    [#input] => 1
                    [#autocomplete_path] => 
                    [#name] => field_ageatdiagnosis[0][value]
                    [#id] => edit-field-ageatdiagnosis-0-value
                    [#value] => 
                    [#sorted] => 1
                )

            [#post] => Array
                (
                )

            [#programmed] => 
            [#tree] => 1
            [#parents] => Array
                (
                    [0] => field_ageatdiagnosis
                    [1] => 0
                )

            [#weight] => 0
            [#processed] => 
            [#sorted] => 1
        )

    [#post] => Array
        (
        )

    [#programmed] => 
    [#parents] => Array
        (
            [0] => field_ageatdiagnosis
        )

    [#weight] => 0.011
    [#processed] => 
    [#sorted] => 1
)

Based on this information, we revised our PHP to set #field_suffix:

<?php
$form
['field_ageatdiagnosis'][0]['value']['#field_suffix'] = t('years');
$form['field_ageatdiagnosis'][0]['value']['#attributes'] = array('class' => 'number inline');
print
drupal_render($form['field_ageatdiagnosis']);
?>

We could set #title and #size the same way.

As shown above, we can even add new classes by modifying the #attributes value (noting that in this case the number class was set in the #attributes array, and we would not want to unset it, so we list it again while adding the class inline):

Note: If you look at the HTML output for a form field, you may see additional classes. For instance, 'form-text' is supplied automatically by Drupal form_rendering, and does not need to be restated when adding a class attribute. Using the drupal_set_message('<pre>'.print_r($form['field_ageatdiagnosis'],TRUE).'</pre>'); trick to see what's there is a very good idea to make sure you don't overwrite any important attribute.

References:

Themeing/Displaying form_set_error messages

Thanks for the great article. I have some problems displaying the form_set_error messages in my new form template -- is there an easy to have them display?
dp

Posted by dario (not verified) on Sat, 2008-03-15 11:54
nana

i have the same problem, any help?

Posted by Visitor on Fri, 2008-05-23 03:46
Make sure you are printing the $messages variable

I'm pretty certain form_set_error messages uses the standard drupal_set_message system, and so will show up so long as your theme outputs the $messages variable in page.tpl.php.

For example, this is from bluemarine's page.tpl.php file:

      <div id="main">
        <?php print $breadcrumb ?>
        <h1 class="title"><?php print $title ?></h1>
        <div class="tabs"><?php print $tabs ?></div>
        <?php print $help ?>
        <?php print $messages ?>
        <?php print $content; ?>
        <?php print $feed_icons; ?>
      </div>

Update: absolutely certain

Posted by Benjamin Melançon on Fri, 2008-05-23 15:13
Post new comment
The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote> <h1> <h2> <h3> <h4> <h5> <h6> <small> <pre> <strike> <sub> <sup>
  • Lines and paragraphs break automatically.
  • Web page addresses and e-mail addresses turn into links automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.