Last updated July 11, 2012. Created by joachim on December 4, 2009.
Edited by westie, bfr, JuliaKM, claar. Log in to edit this page.

This page contains useful notes and code snippets for working with hook_form_alter(). See also the API documentation for example_form_alter() and the Form API Quickstart Guide.

Identifying the form ID

Via HTML
The IDs of form elements can be found by viewing a page's HTML. Search the HTML for "form_id" to find the correct form ID.

Using hook_form_alter() to find a form ID
If you want to alter one form on your site and not every form, you will first need to identify the form's form ID. The following examples assume that you are familiar with creating a module. If this is not the case, review the Module developer's guide. hook_form_alter() needs to be called from within a custom module.

In your custom module, create a function called modulename_form_alter().

<?php
/**
* Implements hook_form_alter().
*/
function mymodule_form_alter(&$form, $form_state, $form_id) {
 
drupal_set_message("Form ID is : " . $form_id);
}
?>

This function will output the following when you visit a page with a form:

Form ID is : your_form_id

You will use the form ID to select and modify a particular form. Note that your page may display more than one form ID. If you have a search form on every page, this form ID will also display. Make sure that you have identified the correct form.

Using hook_form_alter with node edit, user login, and user profile forms

Node edit forms
The form ID for node edit forms is always node_type_node_form (example: 'story_node_form').

Some variations on a theme for detecting node forms in general, on D6:

<?php
 
// override_node_options module
 
if (isset($form['type'])
      &&
     
$form['type']['#value'] .'_node_form' == $form_id) {}
 
// Ubercart. Checks the type is one that we care about.
  // Note that we've got hold of the $node first; this requires checking!
 
$node = $form['#node'];
  if (
is_object($node)
      &&
     
$form_id == $node->type .'_node_form'
     
&&
     
CHECK_OUR_TYPE($node->type)) {}
 
// image module
  // any node type except image
 
if (isset($form['type']['#value'])
      &&
     
$form['type']['#value'] == 'TYPE') {
   
$type = $form['type']['#value'];
   
// If enabled adjust the form.
   
if ($form_id == $type .'_node_form'
       
&&
       
variable_get('image_attach_'. $type, 0)) { }
  }
  if ((
$type = $form['type']['#value'])
       &&
       (
$form_id == $type .'_node_form')) { }
?>

On D7 there's a simpler way:

<?php
 
if (!empty($form['#node_edit_form'])) {
   
// ....
 
}
?>

User login forms
Unlike most forms, the user login form displays in two places and has two different form IDs. If you want to change both user login locations, you will need to target both 'user_login' and 'user_login_block.' user_login is the login form on the /user page. user_login_block is the login form in the user login block.

<?php
 
if($form_id == 'user_login' || $form_id == 'user_login_block') {
   
//do fun stuff here.
 
}
?>

User profile forms
The user profile form ID is 'user_profile_form'. The actual form data is contained within a fieldset whose key is the raw user-entered string (e.g. Main Address). Within hook_form_alter, look at at $form['_category']['#value'] to tell which category you are currently on and to find the key of this fieldset.

Debugging hook_form_alter

hook_form_form_id_alter not overriding a form
hook_form_form_id_alter() can be used when you are overriding a single form. Be advised that hook_form_form_id_alter() will be called before hook_form_alter. As a result, you may not be able to alter something another module has altered.

Another module's hook_form_alter taking precedence
Module weights determine the order in which different modules can alter any form. If you wish to alter elements in a form that has been supplied by another module, you may have to increase the weight of your own module to make it run later.

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

if this were cleaned up a bit, it can be added to the main part of the handbook page above.

I'm posting this to help others looking for examples about using FAPI to make a add user form. Also hoping someone who knows more than I will look at it and suggest alternative (better) ways of doing this.

Had to hack a bit of genpass module. is there a way I can avoid that?:
diff genpass.module themodule.orig
84,85d83
< case 'myaddaleader_form':
< case 'myaddamember_form':

<?php
function genpass_form_alter(&$form, $form_state, $form_id) {
  switch(
$form_id) {
    case
'user_admin_settings':
     
$form['registration']['genpass_mode'] = array(
       
'#type' => 'radios',
       .....
     
$form['#validate'][] = 'genpass_user_admin_settings_validate';
     
$form['#submit'][] = 'genpass_user_admin_settings_submit';
      break;
    case
'myaddaleader_form':
    case
'myaddamember_form':
    case
'user_register':
     
$pass_item =& genpass_get_pass_item($form);
     
// alter the user-admin register (no mode decision)
     
if ($form['destination']['#value'] == 'admin/user/user/create' && user_access('administer users')) {
       
$pass_item['#required'] = FALSE;
       ....
?>

mymodule (yes, I called it mymodule)

<?php
function mymodule_form_alter (&$form, &$form_state, $form_id) {
  switch (
$form_id) {
    case
'group_node_form':
     
//    Set the value to what is desired
     
$form['og_description']['#default_value'] = "a group";
     
// Hide the form item, leave this at the above value
     
$form['og_description']['#access'] = 0;
      break;
    case
'user_register':
     
// use dpm to see the form array
     
return dpm($form);
      break;
    case
'myaddamember_form':
// this next bit is cut and paste from content profile module
     
require_once drupal_get_path('module', 'node') .'/node.pages.inc';
     
// Allow other modules to customize the used profile types, so modules
      // can easily customize the registration form.
     
$default_types = content_profile_get_types('names', (arg(0) == 'admin' ? 'admin_user_create_use' : 'registration_use'));
     
$form += array('#content_profile_registration_use_types' => $default_types);
      foreach (
$form['#content_profile_registration_use_types'] as $type => $typename) {
       
content_profile_registration_add_profile_form($type, $form, $form_state);
      }
// end the bit cut and paste from the content profile module
     
genpass_form_alter($form, $form_state, $form_id);
     
$form['account']['notify']['#default_value'] = TRUE;
     
$form['account']['pass']['#access'] = FALSE;
     
$form['account']['status']['#access'] = FALSE;
     
$form['revision_information']['#access'] = FALSE;
     
$form['comment_settings']['#access'] = FALSE;
     
$form['locale']['#access'] = FALSE;
     
$form['path']['#access'] = FALSE;
     
$form['print']['#access'] = FALSE;
     
$form['book']['#access'] = FALSE;
     
$form['menu']['#access'] = FALSE;
     
$form['group_phone']['field_phone_privacy']['#access'] = FALSE;
     
$form['group_email']['#access'] = FALSE;
     
$form['group_homeaddress']['field_homeaddress_privacy']['#access'] = FALSE;
     
$form['group_homeaddress']['field_homeaddress_public']['#access'] = FALSE;
     
$form['group_homeaddress']['field_homeaddress_leaderviewable']['#access'] = FALSE;
     
$form['group_homeaddress']['field_homeaddress_adminview']['#access'] = FALSE;
      break;
    case
'myaddaleader_form':
// this next bit is cut and paste from content profile module
     
require_once drupal_get_path('module', 'node') .'/node.pages.inc';
     
// Allow other modules to customize the used profile types, so modules
      // can easily customize the registration form.
     
$default_types = content_profile_get_types('names', (arg(0) == 'admin' ? 'admin_user_create_use' : 'registration_use'));
     
$form += array('#content_profile_registration_use_types' => $default_types);
      foreach (
$form['#content_profile_registration_use_types'] as $type => $typename) {
       
content_profile_registration_add_profile_form($type, $form, $form_state);
      }
// end the bit cut and paste from the content profile module
     
genpass_form_alter($form, $form_state, $form_id);
     
$form['account']['notify']['#default_value'] = TRUE;
     
$form['account']['pass']['#access'] = FALSE;
     
$form['account']['status']['#access'] = FALSE;
     
$form['revision_information']['#access'] = FALSE;
     
$form['comment_settings']['#access'] = FALSE;
     
$form['locale']['#access'] = FALSE;
     
$form['path']['#access'] = FALSE;
     
$form['print']['#access'] = FALSE;
     
$form['book']['#access'] = FALSE;
     
$form['menu']['#access'] = FALSE;
     
$form['group_homeaddress']['field_homeaddress_public']['#access'] = FALSE;
     
$form['group_homeaddress']['field_homeaddress_leaderviewable']['#access'] = FALSE;
     
$form['group_homeaddress']['field_homeaddress_adminview']['#access'] = FALSE;
     
// use dpm to see the form array
     
return dpm($form);
      break;
  }
}
function
myaddamember_form($form_state)
{
 
$form=array();
 
// Merge in the default user edit fields.
 
$form = array_merge($form, user_register($form_state, NULL, NULL, TRUE));
// add this role as default, already checked the checkbox
 
$form['account']['roles']['4'] = array(
   
'#type' => checkbox,
   
'#title' => 'Member',
   
'#default_value' => TRUE,
  );
// remove a bunch of roles by setting the allowed options to just these
 
$form['account']['roles']['#options']= array(
   
'4' => 'Member',
   
'5' => 'Leader Applicant',
  );
// make submitting the form actually create a user
 
$form['#submit'][] = 'user_register_submit';
  return
$form;
}
function
myaddaleader_form($form_state)
{
 
$myoptions = array('1' => t('Enabled'), '0' => t('Disabled'));
 
$form=array();
/*
// test helloworld type form building. keeping during testing to remind me how to do this.
  $form['addaleader'] = array(
    '#type' => 'fieldset',
    '#title' => t('Leader account settings'),
    '#tree' => TRUE,
  );
  $form['addaleader']['thefirstoption'] = array(
    '#type' => 'radios',
    '#title' => t('First Option'),
    '#default_value' => variable_get('thefirstoption', 0),
    '#options' => $myoptions,
    '#description' => t('The First Option.'),
  );
// end test helloworld type form building
*/
  // Merge in the default user edit fields.
 
$form = array_merge($form, user_register($form_state, NULL, NULL, TRUE));
 
// check some roles by default
 
$form['account']['roles']['3'] = array(
   
'#type' => checkbox,
   
'#title' => 'Leader',
   
'#default_value' => TRUE,
  );
 
$form['account']['roles']['4'] = array(
   
'#type' => checkbox,
   
'#title' => 'Member',
   
'#default_value' => TRUE,
  );
// keeping to remind me how
//  $form['hidden'] = array('#type' => 'value', '#value' => 'is_it_here');
//  $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
// make submitting the form actually create a user
//  $form['#validate'][] = 'user_register_validate';
 
$form['#submit'][] = 'user_register_submit';
  return
$form;
}
function
mymodule_menu() {
 
$items = array();
// make two new tabs, showing the new create user forms, parallel to the system default add user form
 
$items['admin/user/user/createusermember'] = array(
   
'title' => 'Add user (a Member)',
   
'description' => 'Create a user account for a Member.',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('myaddamember_form'),
   
'access arguments' => array('administer users'),
   
'type' => MENU_LOCAL_TASK,
  );
 
$items['admin/user/user/createuserleader'] = array(
   
'title' => 'Add user (a Leader)',
   
'description' => 'Create a user account for a Leader.',
   
'page callback' => 'drupal_get_form',
   
'page arguments' => array('myaddaleader_form'),
   
'access arguments' => array('administer users'),
   
'type' => MENU_LOCAL_TASK,
  );
  return
$items;
}
?>

no screenshot jpgs, but a quick tour of the forms: http://cathytheys.blip.tv/file/3421680

I want to register new users but I want to validate some of the fields eg "club id" to prevent anybody who is not a club member from registering unless you have an id.
I want the field input to follow a pattern for example AB/XYZ/10/0001 before
the new user will be registered[a legitimate member already knows his/her id].
Is content profile module a right choice if so how?

Use d_printr() to dump the form array values with better formatting

<?php
/**
* Implementation of HOOK_form_alter()
*/
function mymodule_form_alter(&$form, $form_state, $form_id) {
 
drupal_set_message("Form ID is : " . $form_id);
 
drupal_set_message(dprint_r($form, TRUE));
}
?>

It would be really helpful if the user login form example had some real example code in there, besides just //do fun stuff here.
Anyone want to expand upon that please and thanks?
I'm looking for a way to include the words "username" and "password" as default text in the user login form fields. Thanks!

A little bit late... This is some fun stuff you can do ;)

<?php
// Change size
$form['name']['#size'] = 20;
$form['pass']['#size'] = 20;
// Use label as placeholder
$form['name']['#attributes']['placeholder'][] = $form['name']['#title'];
$form['pass']['#attributes']['placeholder'][] = $form['pass']['#title'];
// Remove original Labels
unset($form['name']['#title']);
unset(
$form['pass']['#title']);
?>