Modifying Forms in Drupal 5 and 6

Drupal has a lot of forms to fill out and not all of them may be just the way you want or need them to be. Modifying forms is a topic that is often met with groans but once you understand the two methods to accomplish the task and the basic, underlying concepts, it really isn't that hard to do at all. You'll be a form-modifying, input-customizing wiz in no time. This article will briefly discuss what's going on and then mainly focus on showing working examples for both methods in Drupal 5 and 6. You should be comfortable creating a new function, looking at arrays and a having at least a passing understanding of the Forms API is real handy. Also note that in the examples below I have them wrapped in php tags but you should not include those if you copy/paste. That is just so it looks nice and clear for the article.

Deciding to make the change in the theme or a module

So there are two methods for altering form output in Drupal, one at the theme layer and the other through a custom module. Changes to the HTML can be accomplished with either method so most people will use the method they are more comfortable with already; themers use the theme and developers use a module. There are two situations however where you will want to use a module rather than a theme:

  1. Changing functionality of a form (e.g. adding new validation rules or submission actions) can only happen in a module.
  2. Form elements can be completely removed from the HTML in the theme layer but from an access point of view, someone could still theoretically make use of the field in the form's $_POST because the field is not removed from the form process, only the HTML output is removed. Removing an element in a module can completely remove access to it in any way. This is not often a real problem so removing elements in the theme layer is still acceptable but you should be aware of the distinction.

An example form: User edit

We will not be looking at functional changes here so I'm going to take an example that works well in either method and you can choose the one you prefer. The code is actually quite similar and both rely on two important things: 1) the form's ID and 2) some knowledge of Drupal's Form API.

Our example is altering the form as seen at My Account > Edit in a very simple way to change the name of the Username field from "Username" to say "Login ID" instead.

Find the form ID

All forms in Drupal have a unique ID. You can easily find the form ID in the HTML source code. On your site view the source of the page (Firebug is very handy for this) and look for some hidden fields near the top of the form. One has token stuff in it and another will have the name="form_id". You want to use the value of that form_id input field. In this example it looks something like this (in D5):

  
<input id="edit-user-edit" type="hidden" value="user_edit" name="form_id"/>
  

The value for our form ID in this case is "user_edit". In Drupal 6 the form ID is "user_profile_form".

[Note that if you find the form element, e.g. <form id="user-edit" enctype=...) at the top of the form, you will see that is has a very similar id property (in this case "user-edit"). The thing to be aware of is that the form id property uses a hyphen (-) and the form_id that you need for your work has to use an underscore (_).]

Creating your override functions

Now let's start creating the function we will need to hook into the form. Of course, naming and placing the function is slightly different for themes and modules. In all of these examples replace themename and modulename as appropriate. Here is the starting code:

Option 1: Override the theme function

In the theme layer place your function in your theme's template.php file. You will create a new function for each form you wish to modify.
D5 theme override

  
  /**
   * Theme override for user edit form.
   *
   * The function is named themename_formid.
   */
  function themename_user_edit($form) {
    // Our kickin' mods go here.
  }
  

D6 theme override
In D6 theming this gets interesting. Many forms in D6 now have template files or "registered" functions. For those you can simply edit the .tpl file or override the function as in D5, respectively. For forms that do not have templates or registered functions though, we need to not only create the override function but we also need to register it with the theme system so it knows about it. For more information on this see the handbook page about using the theme registry for special cases. Our user edit form is one such beast. Once you add the following functions to your template.php, reset the the theme registry by clearing cached data on the Administer > Performance page.

Here is the theme registry function we need:

  
/**
 * Implementation of hook_theme.
 *
 * Register custom theme functions.
 */
function themename_theme() {
  return array(
    // The form ID.
    'user_profile_form' => array(
      // Forms always take the form argument.
      'arguments' => array('form' => NULL),
    ),
  );
}
  

And here is the override function; very much like the D5 version except the form ID.

  
  /**
   * Theme override for user edit form.
   *
   * The function is named themename_formid.
   */
  function themename_user_profile_form($form) {
    // Our kickin' mods go here.
  }
  

Option 2: Use hook_form_alter() in a module

D5 hook_form_alter()
If using a module, place the hook_form_alter function (D5 and D6) in your .module file. You will use this one function to change as many forms as you want, using the form ID in a switch statement for each one.

  
  /**
   * Implementation of hook_form_alter().
   *
   * The function is named modulename_form_alter.
   */
  function modulename_form_alter($form_id, &$form) {
    // Normally a switch is used because you may want to alter more than
    // one form and it is easy to add a new case for each form.
    switch ($form_id) {
      // This is our form ID.
      case 'user_edit':
        // Our kickin' mods go here.
        break;
    }
  }
  

D6 hook_form_alter()

  
  /**
   * Implementation of hook_form_alter().
   *
   * The function is named modulename_form_alter.
   */
  function modulename_form_alter(&$form, $form_state, $form_id) {
    // Normally a switch is used because you may want to alter more than
    // one form and it is easy to add a new case for each form.
    switch ($form_id) {
      // This is our form ID.
      case 'user_profile_form':
        // Our kickin' mods go here.
        break;
    }
  }
  

Viewing the $form array

Once you have gotten the form ID and created the function you will need to see the existing form elements you have to work with. If you have Devel module enabled, you can use the shorthand dsm() function to pretty print the $form array. Otherwise a regular PHP print_r() or var_dump() will work fine. Whichever function you use to see the array, place it in your function where the code above says "// Our kickin' mods go here." For example:

  
  function themename_user_edit($form) {
    return print_r($form); 
  }
  

When you reload the page with the form on it now you should see a whole bunch of yummy array output. If not, make sure you got the form_id correct and that you have named your function properly.

Find the form element and property

There is quite a bit of documentation on FAPI and I'm not going to dive very deeply here. Here are the quickstart guides for D5 and D6. You may also want to have a handy bookmark to the form property reference, D5 and D6.

Basically we are going to look at the $form array, find what we want to change and translate that into FAPI for our function. Let's look at a section of the $form array that I am seeing on my site:

  
Array
(
  [account] => Array
  (
    [#type] => fieldset
    [#title] => Account information
    [name] => Array
    (
      [#type] => textfield
      [#title] => Username
      [#weight] => 0
      [#maxlength] => 60
      [#description] => Your preferred username; punctuation is not allowed except for periods, hyphens, and underscores.
    )
  )
  [pass] => Array
  (
    [#type] => password_confirm
    // snip...
  )
  // snip...
  [comment_settings] => Array
  (
    [#type] => fieldset
    [#title] => Comment settings
    [#collapsible] => 1
    [#collapsed] => 
    // snip...
  )
)
  

So basically we need to walk down the array to find the part we want to change. I want to change the title of the Username field. Looking at my array that means I want to target [account][name][#title]. Once I find that, I can just put that in my function and tell Drupal what I want to do with it. If you aren't sure what you can do to a given property, looking at the reference chart linked to above will help out.

  
$form['account']['name']['#title'] = t('Login ID');
  

I can do this with any of the FAPI properties. For example I can set the default value of a fieldset to collapsed:

  
$form['comment_settings']['#collapsed'] = TRUE;
  

Or I can even remove an element entirely using the unset function. This would remove the Signature text area from the form:

  
unset($form['comment_settings']);
  

So, go have fun and get those rascally forms just the way you want them!

Full example functions

Here are the complete, commented example functions.

Theme override: D5

  
/**
 * Theme override for user edit form.
 *
 * The function is named themename_formid.
 */
function themename_user_edit($form) {
  $output = '';
  // Print out the $form array to see all the elements we are working with.
  //$output .= dsm($form);
  // Once I know which part of the array I'm after we can change it.

  // You can use the normal Form API structure to change things, like so:
  // Change the Username field title to Login ID.
  $form['account']['name']['#title'] = t('Login ID');

  // Make sure you call a drupal_render() on the entire $form to make sure you
  // still output all of the elements (particularly hidden ones needed
  // for the form to function properly.)
  $output .= drupal_render($form);
  return $output;
}
  

Theme override: D6

Theme registry function:

  
/**
 * Implementation of hook_theme.
 *
 * Register custom theme functions.
 */
 function themename_theme() {
   return array(
     // The form ID.
     'user_profile_form' => array(
       // Forms always take the form argument.
       'arguments' => array('form' => NULL),
     ),
   );
 }
  

Form override function:

  
/**
 * Theme override for user edit form.
 *
 * The function is named themename_formid.
 */
function themename_user_profile_form($form) {
  $output = '';
  // Print out the $form array to see all the elements we are working with.
  //$output .= dsm($form);
  // Once I know which part of the array I'm after we can change it.

  // You can use the normal Form API structure to change things, like so:
  // Change the Username field title to Login ID.
  $form['account']['name']['#title'] = t('Login ID');

  // Make sure you call a drupal_render() on the entire $form to make sure you
  // still output all of the elements (particularly hidden ones needed
  // for the form to function properly.)
  $output .= drupal_render($form);
  return $output;
}
  

Module hook_form_alter(): D5

  
/**
 * Implementation of hook_form_alter().
 *
 * This lets you make changes to any form in the site. You can alter, remove
 * or add form elements. You can also alter the validation and submission
 * behavior. The name will always be modulename_form_alter.
 */
function modulename_form_alter($form_id, &$form) {
  // Normally a switch is used because you may want to alter more than
  // one form and it is easy to add a new case for each form.
  switch ($form_id) {
    // This is our form ID.
    case 'user_edit':
      // Print out the $form array to see all the elements we are working with.
      //$output .= dsm($form);
      // Once I know which part of the array I'm after we can change it.
      
      // Change the Username field title to Login ID.
      $form['account']['name']['#title'] = t('Login ID');
      break;
  }
}
  

Module hook_form_alter(): D6

  
/**
 * Implementation of hook_form_alter().
 *
 * This lets you make changes to any form in the site. You can alter, remove
 * or add form elements. You can also alter the validation and submission
 * behavior. The name will always be modulename_form_alter.
 */
function modulename_form_alter(&$form, $form_state, $form_id) {
  // Normally a switch is used because you may want to alter more than
  // one form and it is easy to add a new case for each form.
  switch ($form_id) {
    // This is our form ID.
    case 'user_profile_form':
      // Print out the $form array to see all the elements we are working with.
      //$output .= dsm($form);
      // Once I know which part of the array I'm after we can change it.
      
      // Change the Username field title to Login ID.
      $form['account']['name']['#title'] = t('Login ID');
      break;
  }
}
?>

Get in touch with us

Tell us about your project or drop us a line. We'd love to hear from you!