function hook_form_alter

You are here

7 system.api.php hook_form_alter(&$form, &$form_state, $form_id)
4.7 core.php hook_form_alter($form_id, &$form)
5 core.php hook_form_alter($form_id, &$form)
6 core.php hook_form_alter(&$form, &$form_state, $form_id)
8 system.api.php hook_form_alter(&$form, &$form_state, $form_id)

Perform alterations before a form is rendered.

One popular use of this hook is to add form elements to the node form. When altering a node form, the node object can be retrieved from $form['#node'].

Note that instead of hook_form_alter(), which is called for all forms, you can also use hook_form_FORM_ID_alter() to alter a specific form.

Parameters

$form: Nested array of form elements that comprise the form. The arguments that drupal_get_form() was originally called with are available in the array $form['#parameters'].

$form_state: A keyed array containing the current state of the form.

$form_id: String representing the name of the form itself. Typically this is the name of the function that generated the form.

Related topics

15 functions implement hook_form_alter()

Note: this list is generated by pattern matching, so it may include some functions that are not actually implementations of this hook.

book_form_alter in modules/book/book.module
Implementation of hook_form_alter(). Adds the book fieldset to the node form.
color_form_alter in modules/color/color.module
Implementation of hook_form_alter().
comment_form_alter in modules/comment/comment.module
Implementation of hook_form_alter().
default_form_alter in profiles/default/default.profile
Implementation of hook_form_alter().
example_form_alter in developer/example.profile
Implementation of hook_form_alter().

... See full list

2 invocations of hook_form_alter()
drupal_prepare_form in includes/form.inc
Prepares a structured form array by adding required elements, executing any hook_form_alter functions, and optionally inserting a validation token to prevent tampering.
upload_js in modules/upload/upload.module
Menu-callback for JavaScript-based uploads.

File

developer/hooks/core.php, line 754
These are the hooks that are invoked by the Drupal core.

Code

function hook_form_alter(&$form, &$form_state, $form_id) {
  if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] . '_node_form' == $form_id) {
    $path = isset($form['#node']->path) ? $form['#node']->path : NULL;
    $form['path'] = array(
      '#type' => 'fieldset',
      '#title' => t('URL path settings'),
      '#collapsible' => TRUE,
      '#collapsed' => empty($path),
      '#access' => user_access('create url aliases'),
      '#weight' => 30,
    );
    $form['path']['path'] = array(
      '#type' => 'textfield',
      '#default_value' => $path,
      '#maxlength' => 128,
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#description' => t('Optionally specify an alternative URL by which this node can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
    );
    if ($path) {
      $form['path']['pid'] = array(
        '#type' => 'value',
        '#value' => db_result(db_query("SELECT pid FROM {url_alias} WHERE dst = '%s' AND language = '%s'", $path, $form['#node']->language)),
      );
    }
  }
}

Comments

Beware that some info like CCK fields are not in $form when you call this, see http://drupal.org/node/726282 for an explanation.

This hook is useful but documenting how to save/validate these new fields is kinda necessary. Since this document page lacks both of these, here is a quickie example of how to Add a new field, validate it, and then save it. This comment should either be copy/pasted to the d5/d7 doc pages or modified to suit those as well. I dont use either so I cant speak on how things are validated/saved for those versions of hook_form_alter. I assume it's identical.

<?php
function nameOfMyModule_form_alter(&$form, $form_state, $form_id)
{
   if (
$form_id == 'taxonomy_form_term')
   {
  
//Modify the Taxonomy form somehow
   
$form['identification']['new_field_name'] = array(
     
'#type' => 'textfield',
     
'#title' => t('New Field'),
     
'#default_value' => queryable_variables_get('new_field_name', 'tid', $form['#term']['tid']),  //QV's is not a built in drupal function.  Replace it with whatever you use to fill in the value.
     
'#size' => 120,
     
//'#maxlength' => 64,
     
'#description' => t('Describe me u wierdo!'),
      );
     
$form['#validate'][] = 'nameOfMyModule_functionName_validate'; //Be sure not to use 'form' for the 'functionName' part as it will confuse drupal.  Use a descriptive name.
     
$form['#submit'][] = 'nameOfMyModule_functionName_submit'//Be sure not to use 'form' for the 'functionName' part as it will confuse drupal.  Use a descriptive name.
  
}
}

function

nameOfMyModule_functionName_validate($form, &$form_state)
{
  
//Validation stuff here, set $error to true if something went wrong, or however u want to do this.  Completely up to u in how u set errors.
  
if ($error)
   {
     
form_set_error('new_field_name', 'AHH SOMETHING WRONG!');
   }
}

function

nameOfMyModule_functionName_submit($form, &$form_state)
{
   
//Save the new value.
   
queryable_variables_set('new_field_name', $form_state['values']['new_field_name'], 'tid', $form['#term']['tid']);  // I used taxonomy as an example.  How you save data is completely up to u and this is nothing more than a simple example and shouldnt be used as a test.
}
?>

it's possible for '#submit' and '#validate' to be an element of the submit button or some other form element instead of a top-level element of the $form array. (for example, rsvp.module's rsvp_reply_form which i'm currently scratching an itch with)

so instead of

$form['#validate'][] = 'nameOfMyModule_functionName_validate'

you might need

$form['some_element']['#validate'][] = 'nameOfMyModule_functionName_validate'

it's worth finding the #submit and #validate elements in the form you're hooking and following the example

That is the question I asked myself until I discovered the caching settings were essentially bypassing the hook_form_alter process. This is obvious enough, but took me a while to figure this one out. I was attempting to modify a webform block form, and block caching was enabled, hence the form block wasn't being passed through the hook. Makes sense! Hope this helps someone down the road!

Yeah, thats true. In my case, clearing caches through UI or drush didn't work. I emptied the tables 'cache_*' from db and got it working. ;)

The notifications module brought this issue to my attention.

When developers want to modify a form directly, then there is no issue. However, if one module wants to alter the form of another module, it becomes impossible, depending on the order of the modules. For examples, in notifications_ui module:

// in notifications_ui_form_alter()
//...
default:
      if (isset($form['type']['#value']) && $form['type']['#value'] .'_node_form' == $form_id && notifications_ui_node_options($form['type']['#value'], 'form')) {
        //  Add node forms.
        $node = $form['#node'];
        $form[] = notifications_ui_node_subform($node);

In order for the subform to be added, this function alters the form. Assuming the modules are executed in alphabetical order, all modules, alphabetically, before notifications (i.e. nodeference module) will not be able to modify any properties of the notifications_ui_form while those afterwards (i.e. views module) will be able to. Can there be a patch in place that will ensure that developers can modify forms that are created from the core and contributed modules that will not cause any conflicts?

FYI, I have not tested the following solution so I will let you know the results when I do.

Using notifications_ui as the basis, my example of a proposed permanent solution is the following:

        $form['contributed']['notifications'] = notifications_ui_node_subform($node);

The caveat is that all developers include a condition that will check for modifications from another module -or- Drupal core can enforce that through the form_alter function. I welcome any feedback to this especially from those who have experienced this issue.

Instead of adding my modifications to the module, it replaces what I added. The original form,   $form[] = notifications_ui_node_subform($node); , does not work either (as stated earlier). If you were to attempt to do something like $form[0]['subscriptions']['#collapsed'] = FALSE; , it will not work. Any suggestions?

You can reorder module execution with module weight. One of the ways to change modules weights is to install the Utility module.

This way you get off the

all modules, alphabetically, before notifications (i.e. nodeference module) will not be able to modify any properties of the notifications_ui_form

issue.

If you are developing a module you would change your modules weight at install time with something like

function hook_install() {
  $weight = (int) db_result(db_query("SELECT weight
      FROM {system}
      WHERE name = 'taxonomy'
       AND type = 'module';"));
  db_query("UPDATE {system}
    SET weight = %d
    WHERE name = 'YOUR_MODULE_ID'
      AND type = 'module';",
    $weight + 1
  );
}

Any ideas why? When I add fields via form alter or validation fails, my fields added via hook form alter are reset.
This issue may be related: http://drupal.org/node/457630, so I believe this merits some coverage here as well. :)

For example, to add custom checkboxes, or other fields to node forms - it is as simple as the following:

(based on the modulename, nodetype and customfield being used)

function modulename_form_alter(&$form, &$form_state, $form_id){
    if($form_id == 'nodetype_node_form'){
        $form['customfield'] = array(
            '#type' => 'checkbox',
            '#title' => 'Some Custom Field',
            '#default_value' => '0',
        );
        $form['#submit'][] = 'modulename_customfield_submit';
    }
}
function modulename_customfield_submit($form, &$form_state){
    if($form_state['values']['customfield'] == 1){
        //do something based on the value of the custom field
    }
}

<?php
if($form_id == 'nodetype_node_form'){
?>

This targets both the node edit and the content type edit forms. Use the example in the API!!!!

What this module does: puts the user contact form (with an added field) into a block so that the author's contact form is displayed on every node that is created by that user.

So first you have to create a your own custom module. This is the code.

// "customcontact" is name of module.
// "form_alter" is part of drupal and needs to be put there
function customcontact_form_alter(&$form, $form_state, $form_id) {

// only alter user contact forms. (see note 1)
if ($form_id == 'contact_mail_user') {

// add a field to the form (see note 2)
$form['fname'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
'#weight' => -99,
);

//validate field
$form['#validate'][] = 'customcontact_validate';

// submit field
// use array_unshift to submit this field before submitting the form.
array_unshift($form['#submit'], 'customcontact_submit');
}
}

function customcontact_validate($form, &$form_state) {
if ($error) {
form_set_error('fname', 'AHH SOMETHING WRONG!');
}
}
function customcontact_submit($form, &$form_state) {
variable_set('fname', $form_state['values']['fname']);
}

// add the field to the email
function customcontact_mail_alter(&$message) {

// a variable which can be called anything gets the fname field
$field_fname = variable_get('fname', '');

// only alter user contact emails
if ($message['id'] == 'contact_user_mail') {

// change the email subject
$message['subject'] = 'Subject heading';

// add the extra field to the end of the email message
$message['body'][] = $field_fname;

}
}

// create the block
function customcontact_block($op = 'list', $delta = 0, $edit = array()) {

  if ($op == 'list') {
    // Generate listing of blocks from this module, for the admin/block page
    $block = array();
    $block[0]['info'] = t('Company contact form');
  }
  else if ($op == 'view') {
    if ($node = menu_get_object()) {
      // get id of creator of page
      $account = user_load($node->uid);
      // get contact page
      module_load_include('inc', 'contact', 'contact.pages');

      // the heading for the block when displayed on the node
      $block['subject'] = t('Contact %name array('%name' => $account->name);
      // put everything into the block content area
      $block['content'] = drupal_get_form('contact_mail_user', $account);
    }
  }
  return $block;
}

Once the module is created, goto administration > blocks then add the block

note 1: to get the form id. first go to the page with the form and view the source code. look for this code and replace the hyphens with underscores.

<form action="/node/2"  accept-charset="UTF-8" method="post" id="contact-mail-user">

note 2: more elements for each field here http://api.drupal.org/api/drupal/developer--topics--forms_api_reference....

If i understand correctly;
When i put a hook_alter_form in a module, I can use switch() and case to specify the form and actions. I can also use case to specify altering of different forms.

Can i just create a module with the hook and the appropriate $form and have it picked up wherever the form is rendered? Can i have one master module that represents all my alters?, or do i need to add the code in each individual module that i am looking to alter?

Also, can someone point me to some documentation on how to use switch() and case to to filter on $user role?

Thanks much

In Drupal 7, it looks like form_id is an array and no longer a String.

If you need to include the cck fields in the form altering process, then use hook_form_alter() instead of hook_form_FORM_ID_alter().

For some reason, when I added a submit callback to form 'user_profile_form', I had to also add the default submission handler (function user_profile_form_submit). Otherwise, the form wouldn't be saved. Did not have to do this on *_node_form.

<?php
function example_form_alter(&$form, &$form_state, $form_id) {
  if (
$form_id == 'user_profile_form') {

   

// On form submission, run our function to do additional work.
   
$form['submit']['#submit'][] = 'example_extra_function_run_on_user_submission';

   

// NOTE! Need to also add user_profile_form_submit, or else the the user object
    // won't be saved at all.
   
$form['submit']['#submit'][] = 'user_profile_form_submit';
  }
}
?>

Hi all,
I have a webform under Drupal 6 to subscribe to a schedule for open days.
I wrote a hook_form_alter function which calculates how many places remains for each schedule, and I want to disable the radio button (and only this one) where there is no more places.
How can I do this ?

Thank you by advance

See the discussion at: http://drupal.org/node/284917

For D7+ you have this already: http://drupal.org/node/342316#comment-4732130

But for Drupal 6, what is preventing you from adding an #after_build and disabling or restricting access per option? Select lists do not support this (all or nothing), but the others simply require you to set the #access on the radio / checkbox child to false in the after build.