Customizing Drupal Forms: What I just learned

I've been working on a project that required some pretty heavy-duty customizations to how the user registration form looks and while getting everything to work I learned a few things that have changed the way I look at customizing forms in Drupal. Here is a quick summary of what I already new, and what I know now.

Altering forms

Ok, this part I knew already... It's pretty easy to change certain aspects of your form elements from within a module by using either hook_form_alter(&$form, &$form_state, $form_id) or by using hook_form_FORM_ID_alter(&$form, &$form_state)

Here is a quick example of removing the title from a form element so that no <label> tag appears when the form renders:

  function mymodule_form_alter(&$form, &$form_state, $form_id) {
    if ($form_id == 'the-form-I-want-to-alter') {
      $form['birthday']['#title'] = '';
      }
    }

Theming forms

Here is one that I didn't know: every form already has a theme function pre-defined. For instance, if you want to change how the user registration form (form_id: 'user_register') is themed, then simply create a function in your theme's template.php called mytheme_user_register($form){ ... }. One important thing to remember though is that this function must return the rendered version of your form. Here is a simple example:

mytheme_user_register($form) {
  $output = '';
  $output .= '
'; $output .= drupal_render($form); $output .= '

'; return $output; }

note: you still need to regster your mytheme_user_register function in hook_theme() and then clear your cache.

Rendering forms

My most interesting discovery was about the drupal_render function. If you use drupal_render to render individual elements within your theme function, then when you call drupal_render($form) it will only render the elements you haven't already rendered. (WOW "render" 5 times in one sentence!). For an example of what I mean, check out the theme_user_admin_perm function. Basically, the function is using drupal_render on each permission checkbox and adding that to a $row, which ultimately gets sent to theme('table'). Finally, the drupal_render($form) call renders the rest of the form - whatever is left - like the submit button.