Last updated August 23, 2009. Created on May 15, 2007.
Edited by kanani, MGParisi, Morbus Iff, dldege. Log in to edit this page.

FormAPI now uses the $form_state variable.

Previous versions of FormAPI used a combination of $form_values, global variables, and custom flags in the form definition itself to capture information about the form's workflow and its current state during processing. In Drupal 6, a single array -- $form_state -- is passed by reference along through each stage of form processing. Several standard $form_state keys are used in FormAPI by default:

  • $form_state['values']
    Incoming $_POST data is first sanitized and checked against the structure of the form before being handed off to validate and submit handlers. The 'values' key is used to store this collection of data. This key replaces the old separate $form_values variable that was passed to submit and validate handlers.
  • The following three keys can be used to control the rendering and processing workflow of the form. Validation and submission handlers can modify the data in these three keys to alter the form workflow based on user input.
  • $form_state['redirect']
    The 'redirect' key controls what happens after a form's processing is complete. By default, the page with the form on it will reload, so the form's fields can be cleared out. If 'redirect' is set to a Drupal path (like user/edit), the user will be redirected to that path instead. If 'redirect' is set to FALSE, the user will not be redirected after the form is processed -- the values they entered into the form will remain in the fields.
  • $form_state['rebuild']
    The 'rebuild' key overrides the 'redirect' key: when it is set to TRUE, the form will be rebuilt from scratch and displayed on screen. This gives form construction code a chance to add additional fields or alter the structure of the form based on user input (For example, re-building the form with additional fields if the user clicks 'give me more choices'). If this flag is set by a validation handler, any 'submit' handlers will be skipped. If it's set by a 'submit' handler, the form will be rebuilt and displayed after all submit handlers have finished processing.
  • $form_state['storage']
    When building complex forms that require multiple steps for completion (for example, a three-page survey), it's necessary to preserve the data from all steps so that they can be processed together at the end. (And, on occasion, to vary the contents of one step based on the input from the previous one). Any data placed in the 'storage' bin of the $form_state collection will automatically be cached and re-loaded when the form is next submitted, allowing your code to accumulate data from step to step and process it in the final stage without any additional code. Developers who want more control can use their own caching mechanisms to store temporary form data (the user session and hidden form fields are two popular alternatives), but the 'storage' bin is automatically handled for you by FormAPI.
    Note that if $form_state['storage'] is populated, $form_state['rebuild'] is automatically set to TRUE.
  • $form_state['submitted']
    $form_state['submit_handlers']
    $form_state['validate_handlers']

    These three keys store information about the current processing state of the form. If 'submitted' is TRUE, user input is currently being processed. The 'submit_handlers' and 'validate_handlers' keys hold any custom validation or submission handlers that were attached to the specific button clicked by the user.
  • $form_state['clicked_button']
    A full copy of the button element that was clicked to submit the form. This is more reliable than the old $form_values['op'] name, and also carries any additional information that was placed in the button element's form definition.

Validation and submission handlers can place additional data in custom bins in the $form_state.

Some uses of #type 'value' are deprecated

Previously, references to static (often complex) variables that are required in form validation, submission, or theming were placed into a form element of the type 'value'. However, form elements of type 'value' are needlessly processed into $form_state['values']. You can simply store arbitrary variables in $form['#foo'] instead, as long as '#foo' does not conflict with any other internal property of the Form API.

Drupal 5:

<?php
function my_form() {
 
$variable = my_get_variable();
 
$form['my_value'] = array(
   
'#type' => 'value',
   
'#value' => $variable,
  );
  return
$form;
}
?>

Drupal 6:

<?php
function my_form() {
 
$variable = my_get_variable();
 
$form['#my_value'] = $variable;
  return
$form;
}
?>

Parameters for form validation and submission functions have changed

Previously, $form-id_validate and $form-id_submit() functions received the $form_id and $form_values parameters. Now, they receive $form and $form_state. If the form's ID is needed, it can still be retrieved from $form['form_id']['#value'], or $form_state['values']['form_id']. In most cases, however, that ID is unneeded.

Drupal 5:

<?php
function my_form_validate($form_id, $form_values) {
 
// validation code goes here...
 
if ($form_values['name']) {
   
// etc. ...
 
}
}
?>

Drupal 6:

<?php
function my_form_validate($form, &$form_state) {
 
// validation code goes here...
 
if ($form_state['values']['name']) {
   
// etc. ...
 
}
}
?>

Please don't forget to call $form_state by reference by prepending the ampersand character (&), as changes to $form_state would otherwise get lost.

Parameters for hook_form_alter() have changed

Similar syntax changes have been made to hook_form_alter(), though it still preserves the $form_id parameter given its use as a central function that operates on many different forms. In addition, $form is now the first parameter rather than the second, to preserve consistency with other hook_$foo_alter() functions throughout core.

Drupal 5:

<?php
function my_module_form_alter($form_id, &$form) {
  if (
$form_id == 'my_form') {
   
// Form modification code goes here.
 
}
}
?>

Drupal 6:

<?php
function my_module_form_alter(&$form, $form_state, $form_id) {
  if (
$form_id == 'my_form') {
   
// Form modification code goes here; $form_state cannot be modified,
    // but decisions can be made based on its contents.
 
}
}
?>

hook_form_alter() complemented by hook_form_$form-id_alter()

Optionally, modules can implement form-specific alteration functions rather than a single hook_form_alter() with many conditional switch statements. This is optional, and is most useful for tidying the code of modules that alter many forms to customize a site's operations.

Note that hook_form_$form-id_alter() is called before all hook_form_alter() regardless of module weight. This means that if you want to edit CCK $form elements, you have to use hook_form_alter() instead of hook_form_$form-id_alter().

Drupal 5:

<?php
function my_module_form_alter($form_id, &$form) {
  switch (
$form_id) {
    case
'my_form':
   
// Form modification code goes here.
   
break;

    case
'my_second_form':
   
// Form modification code goes here.
   
break;

    case
'my_third_form':
   
// Form modification code goes here.
   
break;
  }
}
?>

Drupal 6 :

<?php
function my_module_form_alter(&$form, &$form_state, $form_id) {
  switch (
$form_id) {
    case
'my_form':
   
// Form modification code goes here.
   
break;

    case
'my_second_form':
   
// Form modification code goes here.
   
break;


    case
'my_third_form':
   
// Form modification code goes here.
   
break;
  }
}
?>

Or new optional format

<?php
function my_module_form_my_form_alter(&$form, &$form_state) {
 
// Form modification code goes here.
}

function
my_module_form_my_second_form_alter(&$form, &$form_state) {
 
// Form modification code goes here.
}

function
my_module_form_my_third_form_alter(&$form, &$form_state) {
 
// Form modification code goes here.
}
?>

the above function names are built using the following format: [your-module-name]_form_[$form_id]_alter.

Submit handlers use $form_state rather than returning urls

In previous versions, a Drupal path was returned by a submit function in order to indicate that the user should be redirected to a new page after processing. In Drupal 6, the $form_state collection is used to control the processing of a form. Instead of returning a path, submit handlers can populate $form_state['redirect'] with a path.

In addition, developers using submit functions to persist data to the database are encouraged to return meaningful data about the results of the operation (ids of newly created nodes, etc.) in the $form_state. This allows developers calling forms using drupal_execute() to properly handle to the results of a form's successful submission.

Drupal 5:

<?php
function my_form_submit($form_id, $form_values) {
 
// processing code goes here...
 
return 'admin/my_page';
}
?>

Drupal 6:

<?php
function my_form_submit($form, &$form_state) {
 
// processing code goes here...

  // set the redirect
 
$form_state['redirect'] = 'admin/my_page';

 
// passing the new {node}.nid for use by drupal_execute or other submit handlers
 
$form_state['nid'] = $node->nid;
}
?>

Remember not to forget to call $form_state by reference by prepending the ampersand character (&), as changes to $form_state would otherwise get lost.

$form['#submit'] and $form['#validate'] and $form['#process'] no longer support custom parameters

The #validate and #submit properties on a form can be used to add custom validation and submission handlers to a form, beyond the default $form-id_validate() and $form-id_submit() function names. Previously, it was possible to define custom arguments to pass into each of those parameters. Since the entire form array is now passed to each of those functions, additional parameters are unnecessary (same with #process).

Drupal 5:

<?php
function my_form() {
 
// form definition code goes here...
 
$form['#submit']['my_submit_function'] = array($param1, $param2);
  return
$form;
}
?>

Drupal 6:

<?php
function my_form() {
 
// form definition code goes here...
 
$form['#submit'][] = 'my_submit_function';
 
$form['#my_form_param1'] = $param1;
 
$form['#my_form_param2'] = $param2;
}
?>

When altering a form to add an additional submit handler, similar changes must be made:

Drupal 5:

<?php
function my_module_form_alter($form_id, &$form) {
  if (
$form_id == 'my_form') {
   
$form['#submit']['my_additional_submit_handler'] = array();
  }
}
?>

Drupal 6:

<?php
function my_module_form_alter(&$form, $form_state, $form_id) {
  if (
$form_id == 'my_form') {
   
$form['#submit'][] = 'my_additional_submit_handler';
  }
}
?>

form_set_value() parameters have changed

form_set_value(), like most other functions, now accepts the $form_state parameter. Adding this parameter to the end of every call to form_set_value() is required, as $form_state has replaced the internal global variables that were previously used to store form values.

Drupal 5:

<?php
form_set_value
($form_element, 'value');
?>

Drupal 6:

<?php
form_set_value
($form_element, 'value', $form_state);
?>

#multistep is gone, use $form_state instead

In previous versions of Drupal, forms that looped, repeatedly adding more fields or displaying different options based on previous user input, used a special #multistep property in the form definition. This behavior is no longer controlled by an explicit property, but by the contents of the $form_state collection.

When either a validation or a submission handler has completed their work, setting $form_state['rebuild'] to TRUE will cause the form to be rebuilt and rendered in a manner similar to that triggered by Drupal 5's #multistep property. The complete contents of $form_state are given to your form constructor function when the form is rebuilt, including $form_state['values'] for incoming form values and any other data stored in $form_state by your validation or submission handlers.

Also note that $form_values was previously passed in at the end of the list of parameters for your form constructor function, while $form_state is passed in before any additional parameters. Now that $form_state is passed into every form constructor function, it appears first in the list of parameters.

Drupal 5:

<?php
function my_form($param1, $form_values = NULL) {
 
// standard form definition code goes here...
 
$form['#multistep'] = TRUE;
 
$form['#redirect'] = FALSE;

  if (!empty(
$form_values)) {
   
// We're building the form a second time based on previous input...
    // Add additional fields, etc.
 
}
}


function
my_form_submit($form_id, $form_values) {
 
// process the form input
}
?>

Drupal 6:

<?php
function my_form($form_state, $param1) {
 
// standard form definition code goes here...

 
if (!empty($form_state)) {
   
$submitted_data = $form_state['values'];
   
$my_custom_stuff = $form_state['my_data'];

   
// We're building the form a second time based on previous input...
    // Add additional fields, etc.
 
}
}

function
my_form_submit($form, &$form_state) {
 
// process the form input...
 
$form_state['rebuild'] = TRUE;
 
$form_state['my_data'] =  'this will be passed back to the form constructor when the form is rebuilt...';
}
?>

In addition, a 'storage' element in the $form_state collection allows submission handlers to save temporary data between page loads, until a form's processing is completed. This allows forms that use multiple steps to build up a single data structure (like a complex survey, or data that must be persisted in a single batch) to accumulate information in $form_state['storage'], allowing FormAPI to keep track of it until the time comes to persist it properly in the submission handler during the form's final step.

If $form_state['storage'] is populated by any submit handlers, $form_state['rebuild'] is automatically set to true, and the data in $form_state['storage'] is cached and retrieved each time the page is re-loaded.

Validation for specific form elements now uses the #element_validate property

Previously, it was possible to set the #validate property on any form element, and the specified validation handlers would evaluate any data submitted in the element. This was most useful when defining custom form elements that needed special validation rules at all times (password validation fields, date fields, etc.).

However, #validate behaved differently if the property was set on a specific element rather than the entire form. While form-level validation received $form_id and $form_values as parameters, individual elements received $form_id and the entire $form array, without the $form_values collection. Due to the differences in behavior between these two modes, the functions have been separated into #validate and #element_validate.

When setting form-level validation functions, use #validate. When adding custom validation rules to specific form elements, use #element_validate.

Drupal 5:

<?php
function my_form() {
 
// standard form definition code goes here...
 
$form['#validate']['my_custom_validate'] = array(); 
}

function
my_module_elements() {
  return array(
   
'my_custom_form_element' => array(
      
'#custom_property' => 'foo',
      
'#validate' => array('my_element_validate' => array()),
     );
  );
}
?>

Drupal 6:

<?php
function my_form() {
 
// standard form definition code goes here...
 
$form['#validate'][] = 'my_custom_validate'
}

function
my_module_elements() {
  return array(
   
'my_custom_form_element' => array(
      
'#custom_property' => 'foo',
      
'#element_validate' => array('my_element_validate'),
     );
  );
}
?>

$form['#base'] is gone

In FormAPI, many forms with different form_ids can share the same validate, submit, and theme handlers. This can be done by manually populating the $form['#submit'], $form['#validate'], and $form['#theme'] elements with the proper function names.

Previously, it was also possible to do this by setting $form['#base'] to a shared ID across all of the forms. In Drupal 6 and later, the use of #base has been eliminated for the sake of simplicity.

Drupal 4.7, 5:

<?php
function my_shared_form() {
 
$form['#base'] = 'my_shared_form_id';
 
// more form stuff here...
 
return $form;
}
?>

Drupal 6:

<?php
function my_shared_form() {
 
$form['#submit'][] = 'my_shared_form_submit';
 
$form['#validate'][] = 'my_shared_form_validate';
 
$form['#theme'] = 'my_shared_form'; // this will be called as theme('my_shared_form').
  // more form stuff here...
 
return $form;
}
?>

This is the same format used when modifying forms with hook_form_alter, and the clarity and consistency should help eliminate some confusion.

#post_render complements #pre_render

Previously, the #pre_render property of a form element was used to finesse form elements just before their output as HTML. This can now be now applied to all renderable elements (not just form elements), and it is complemented by a similar #post_render property, which allows modules to modify the output of an element after it has been rendered.

Form buttons can define custom #submit and #validate handlers

All forms can have #validate and #submit properties containing lists of validation and submission handlers to be executed when a user submits data. Previously, if a form featured multiple submission buttons to initiate different actions (updating a record versus deleting, for example), it was necessary to check the incoming $form_values['op'] for the name of the clicked button, then execute different code based on its value.

Now, it is possible to define #validate and #submit properties on each individual form button if desired.

When a specific button is used to submit a form, its validation and submission handlers will be used rather than the default form-level ones. If none are specified at the button level, the form-level handlers will be used instead.

Drupal 5:

<?php
function my_form() {
 
// standard form definition code goes here...
 
$form['delete'] = array(
   
'#type' => 'submit',
   
'#value' => t('Delete'),
  );
 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => t('Submit'),
  );
}


function
my_form_submit($form_id, $form_values) {
  if (
$form_values['op'] == t('Delete') {
   
// Delete the record
 
}
  else {
   
// Update the record
 
}
}
?>

Drupal 6:

<?php
function my_form() {
 
// standard form definition code goes here...
 
$form['delete'] = array(
   
'#type' => 'submit',
   
'#value' => t('Delete'),
   
'#submit' => array('my_delete_function'),
  );
 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => t('Submit'),
  );
}

function
my_form_submit($form, $form_state) {
   
// Update the record
}

function
my_delete_function($form, $form_state) {
   
// Delete the record
}
?>

The 'op' element in the form values is deprecated and should not be relied upon

As discussed above, each button can have #validate and #submit functions associated with it. Thus, there should be one button that submits the form and which invokes the normal $form_id_validate and $form_id_submit handlers. Any additional buttons which need to invoke different validate or submit functionality should have button-specific functions. Note also that the 'op' element in the form values, corresponding to the button clicked when there are several in a form in Drupal 5.x, should no longer be relied upon and may not be present.

Validation handlers can pass information to submit handlers

Validation handlers must sometimes perform complex, time-consuming, or resource-intensive operations to properly validate data. Repeating these expensive operations in the submission handlers is undesirable. Previously, it was necessary to embed these results in the $form_values to carry them over from validation to submission. Now, though, validation handlers can simply place them into $form_state.
This keeps the new $form_state['values'] clean, and ensures that it always represents the data submitted by the web user, rather than a general storage bin for temporary information. It also eliminates the need to create dummy 'hidden' fields in forms to serve as storage bins for such data.

Drupal 5:

<?php
function my_form_validate($form_id, $form_values, $form) {
 
// contact a web service that charges per-request!
 
if ($results) {
   
form_set_value($form['my_dummy_element'], $results);
  }
  else {
   
form_set_error('form', t('The results were incorrect!');
  }
}

function
my_form_submit($form_id, $form_values) {
 
$results = $form_values['my_dummy_element'];
 
// process the results
}
?>

Drupal 6:

<?php
function my_form_validate($form, &$form_state) {
 
// contact a web service that charges per-request!
 
if ($results) {
   
$form_state['expensive_results'] = $results;
  }
  else {
   
form_set_error('form', t('The results were incorrect!');
  }
}

function
my_form_submit($form, &$form_state) {
 
$results = $form_state['expensive_results'];
 
// process the results
}
?>

Parameters for drupal_retrieve_form() have changed

drupal_retrieve_form(), like most other functions, now accepts the $form_state parameter. Adding this parameter to the end of every call to drupal_retrieve_form() is required, as $form_state has replaced the internal global variables that were previously used to store form values.

Drupal 5:

<?php
$my_form
= drupal_retrieve_form('my_form_id');
?>

Drupal 6:

<?php
$my_form_state
= array('storage' => NULL, 'submitted' => FALSE);
$my_form_state['values']['name'] = $name;
$my_form = drupal_retrieve_form('my_form_id', $my_form_state);
?>

Parameters for hook_forms() have changed

Because the $form_id was so frequently accessed in implementations of hook_forms(), it's now been made the first parameter with all additional arguments being passed in as the second argument.

Drupal 5:

<?php
function my_module_forms($args) {
  if (
$args[0] == 'my_form_id') {
   
// return forms.
 
}
}
?>

Drupal 6:

<?php
function my_module_forms($form_id, $args) {
  if (
$form_id == 'my_form_id') {
   
// return forms.
 
}
}
?>

Parameters for #pre_render functions have changed

#pre_render has been abstracted to the generalized rendering process in drupal_render(), and as such, it now accepts an array of elements to render, rather than form properties specifically.

Drupal 5:

<?php
...
$form['#pre_render']['example_prerender'] = array();
...

function
example_prerender($form_id, &$form) {
  if (
$form_id == 'some_form') {
  ...
  }
}
?>

Drupal 6:

<?php
...
$form['#pre_render'][] = 'example_prerender';
...

function
example_prerender($form) {
  if (
$form['#id'] == 'some_form') {
  ...
  }
}
?>

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

Comments

coofercat’s picture

There are some subtle differences between FAPI in Drupal 5 and 6, that may have hefty ramifications for module code.

If you were doing multi-step forms in Drupal 5, you may have been passing a "step" between form pages as a hidden form field (like many examples on the web suggest). You may then have used the function that returned the form to decide what form to return, based on posted form data (which you'd have got from $form_values). This can still be made to work, but is confusing if there was a verification error.

The reason this is a subtle problem is because in Drupal 5, a verification error would cause FAPI to reload the form, using the previous form_values. That meant that the form function would make the same decision about which form to display. In Drupal 6, it seems the form is loaded with the just submitted information, so will make the wrong decision about which form to display.

The way to resolve this is to make any multistep step decisions in the submit handler. It won't be called if there's a verification error, so will only change the form step when appropriate. Further, you can store the step in $form_state['storage'], so there's no need to have a hidden form field.

(Incidentally, $form['#tree'] is still around (just in case you thought that might be gone too!)

douggreen’s picture

#validate, #submit, AND #process handlers all have a final argument so $form_state.

shunting’s picture

Many thanks to the developers for meeting the requirements, big time for making it easier to iterate through different states of the same form. The two-stage #multistep thing was making me tear out what little hair I have left. This covers some of the easier aspects of the new FAPI; maybe it will help others get up to speed.

/* test_fapi_edit is a menu callback */

/* $form_state is NULL when the form is first built, then populated through the
   life-cycle of the form, here by the *_submit hook */

function test_fapi_edit($form_state=NULL) {

  drupal_set_message('test_fapi_edit');

  $form['fapi'] = array(
    '#title'=>'Simple tests of FAPI in D6',
    '#type'=>'fieldset',
    '#collapsible'=>TRUE,
    '#collapsed' => FALSE,
  );

  // Use value from storage bin if present, otherwise use the default
  $form['fapi']['test_textfield'] = array(
    '#type' => 'textfield',
    '#title' => t('Text'),
    '#default_value' =>  isset($form_state['storage']['stored_textfield']) ? $form_state['storage']['stored_textfield'] : '[please enter a value]',
  );
  $options = array(
      'red' =>   'Red',
      'green' => 'Green',
      'blue' =>  'Blue',
  );

  // New radios are added by the rebuild button. Storage will be empty when form is first built.
  if ($form_state['rebuild'] == TRUE && isset($form_state['storage']['new_radios'])) {
      $options = array_merge($options,$form_state['storage']['new_radios']);
  };

  $form['fapi']['test_radios'] = array(
    '#type' => 'radios',
    '#title' => t('Font Color'),
    '#default_value' =>  isset($form_state['storage']['stored_radios']) ? $form_state['storage']['stored_radios'] : 'red',
    '#options' => $options,
    );

  // Default value for checkboxes is an array (string gives that pesky 'Invalid argument supplied for foreach()' error).
  $form['fapi']['test_checkboxes'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Font style'),
    '#default_value' =>  isset($form_state['storage']['stored_checkboxes']) ? $form_state['storage']['stored_checkboxes'] : array('bold'),
    '#options' => array(
      'bold' =>   'Bold',
      'italic' => 'Italic',
    ));

  // Now the buttons. Note use of 'Save' rather than 'Submit.'

  // Resets all values to defaults
  $form['fapi']['reset'] = array(
    '#type' => 'submit',
    '#value' => t('Reset'),
  );

  // Shows entered values being preserved across submits
  $form['fapi']['storage'] = array(
    '#type' => 'submit',
    '#value' => t('Test Storage'),
  );

  // Shows new components of the form being added
  $form['fapi']['rebuild'] = array(
    '#type' => 'submit',
    '#value' => t('Test Rebuild'),
  );

  // Would save and redirect (if that was added)
  $form['fapi']['save'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  return $form;

}

function test_fapi_edit_validate($form, &$form_state) {
  // Messages to show form state values being accessed
  drupal_set_message('test_fapi_edit_validate');
  drupal_set_message('Clicked: '   . $form_state['clicked_button']['#value']);
  drupal_set_message('Textfield: ' . $form_state['values']['test_textfield'] . '.');
  drupal_set_message('Radio: '     . $form_state['values']['test_radios'] . '.');

  $checked = array_intersect(array_keys($form_state['values']['test_checkboxes']),array_values($form_state['values']['test_checkboxes']));
  drupal_set_message('Checkboxes: ' . implode($checked,', ') . '.');
}

function test_fapi_edit_submit($form, &$form_state) {
  $clicked = $form_state['clicked_button']['#value'];

  drupal_set_message('test_fapi_edit_submit: ' . $clicked);

  switch ($clicked) {
    case 'Reset':
      // Reset all values to defaults
      $form_state['rebuild'] = TRUE;
      unset($form_state['storage']);
      break;
    case 'Test Storage':
      // Preserve entered values in 'bins' through the life-cycle of the form
      $form_state['storage'] = array(
          'stored_textfield' =>  $form_state['values']['test_textfield'],
          'stored_radios' =>     $form_state['values']['test_radios'],
          'stored_checkboxes' => $form_state['values']['test_checkboxes'],
          );
      break;
    case 'Test Rebuild':
      // Add new components to the form
      $form_state['rebuild'] = TRUE;
      $form_state['storage'] = array(
        'new_radios' => array('cyan'  => 'Cyan','magenta' => 'Magenta','yellow'  => 'Yellow'));
      break;
    case 'Save':
      // Save and redirect (with database storage functions and the path that
      // would normally be entered here; since no value we go to the home page)
      $form_state['redirect'] = '';
      break;
    default:
      drupal_set_message('This can\'t happen.');
      break;
    }
  return;
}

function theme_test_fapi_edit(&$form) {
  $themed_output .= drupal_render($form['fapi']);
  return drupal_render($themed_output);
}

Again, many thanks to the developers and the Drupal community. The new FAPI is reason enough to go for D6, IMNSHO.

http://www.universalpantograph.com

rszrama’s picture

It should be noted that a change in this point applies to all forms... namely that whether you're trying to make a multi-step form or not, the first parameter passed to a form will always be $form_state. This screwed me up for a while b/c I couldn't figure out why in the world drupal_get_form() wasn't passing my parameters correctly.

So, every form builder function should use...

<?php
 
function example_form($form_state, $arg1, $arg2, ...) {
  }
?>

I'm curious, should that be just $form_state or should it be &$form_state like it is for validate/submit?

----------------------
Drupal by Wombats | Current Drupal project: http://www.ubercart.org

------------------------------------------
Read more: http://ryanszrama.com/topics/drupal

kiamlaluno’s picture

The system.module uses code like the following:

<?php
/**
 * Form builder; display theme configuration for entire site and individual themes.
 *
 * @param $key
 *   A theme name.
 * @return
 *   The form structure.
 * @ingroup forms
 * @see system_theme_settings_submit()
 */
function system_theme_settings(&$form_state, $key = '') {
 
//...
}
?>

-- Kiam@AVPnet

Kiam
Kiam la luno renkontas la sunon

timtriche’s picture

I noticed that in the 6.x FAPI, a node creation form has its theme defaulted to

<?php
$form
['#theme'] = array(
  [
0] => modulename_node_form,
  [
1] => node_form
);
?>

If you've been using $form['#theme'] to point at a form theme (in my case, $form['#theme'] = 'survey_form' and you've either not registered your modulename_node_form theme, or not realized that your theme specification is being ignored, it can take some time to see what's going on.

Hope this helps someone (thx Sepeck on IRC for reminding me to add comments to handbook pages.)

emok’s picture

It does not say how to set a querystring for $form_state['redirect'] above or in the documentation I read. (Please improve it if you can.)
I found that one way is to use an array, with the arguments that should be passed to drupal_goto(), namely $path, $query, $fragment.

<?php
 
//Different kinds of value for form-redirection:
$form_state['redirect'] = FALSE; // Prevents redirection
$form_state['redirect'] = "node/15"; // A string value redirects to a Drupal path without querystring or fragment
$form_state['redirect'] = array("node/15", "page=5", "comment-331") // An array is passed as arguments to drupal_goto()
// which here results in the URL "node/15?page=5#comment-331".
// Setting that string directly as $form_state['redirect'] = "node/15?page=5#comment-331" does NOT work!
?>
coupet’s picture

Tutorial: Ten Step-by-Step Code Samples to Help You Quickly Learn Form API
http://drupal.org/node/262422

----
Darly

----
Darly

kswan’s picture

Note regarding http://drupal.org/comment/reply/144132#form-id-alter, that hook_form_$form-id_alter() is called before all hook_form_alter() regardless of module weight.

If you want to edit CCK $form elements, you have to use hook_form_alter() instead of hook_form_$form-id_alter().

amanire’s picture

This is a valuable bit of information. I spent hours trying to pass a default value to a node reference CCK field. Thanks!

SangersDrupalDude’s picture

I managed to set the default value like this:

function mymodulename_form_alter(&$form, &$form_state, $form_id) {
switch ($form_id) {
case 'mycck_node_form':;

$form['field_publication_authors'] = array(
'#type' => 'textfield',
'#title' => t('changed the text of label field_publication_authors'),
'#default_value' => 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
'#size' => 60,
'#maxlength' => 128,
'#required' => TRUE,
);

.....

Senior Drupal Web Developer
Wellcome Trust Sanger Institute

dyba’s picture

Under the header "Form buttons can define custom #submit and #validate handlers", I believe there is a missing character '&' in my_form_submit. It should read function 'my_form_submit($form, &$form_state)' and NOT 'function my_form_submit($form, $form_state)'

<?php
function my_form() {
 
// standard form definition code goes here...
 
$form['delete'] = array(
   
'#type' => 'submit',
   
'#value' => t('Delete'),
   
'#submit' => array('my_delete_function'),
  );
 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => t('Submit'),
  );
}

function
my_form_submit($form, $form_state) {
   
// Update the record
}

function
my_delete_function($form, $form_state) {
   
// Delete the record
}
?>

Daniel Dyba
Email: dyba.drupal@gmail.com

lakshmi_drupal2’s picture

On this page, under "$form['#submit'] and $form['#validate'] and $form['#process'] no longer support custom parameters", for node forms, I believe this code

<?php
function my_module_form_alter(&$form, $form_state, $form_id) {
  if (
$form_id == 'my_form') {
   
$form['#submit'][] = 'my_additional_submit_handler';
  }
}
?>

should read

<?php
function my_module_form_alter(&$form, $form_state, $form_id) {
  if (
$form_id == 'my_form') {
   
$form['buttons']['submit']['#submit'][] = 'my_additional_submit_handler';
  }
}
?>

because otherwise, I found that node_form_submit is called after my_additional_submit_handler() causing $form_state['redirect'] (that might be set in the additional submit handler) to be overwritten by 'node/nnn' for the newly created node.

I found my answer at http://www.brianvuyk.com/story-type/changing-redirect-value-drupal-node-...

scarer’s picture

Found out what the problem was. Don't include this on your submit button for drupal 6 and it should work:

'#submit' => TRUE,