Drupal Forms API

Under construction.

From the Forms API quickstart guide:

The Drupal forms API is a powerful leap forward. It also allows for almost unlimited possibilities for custom theming, validation, and execution of forms. Even better, ANY form (even those in core) can be altered in almost any way imaginable--elements can be removed, added, and rearranged.

Forms API goes by the moniker FAPI.

Example scaffolding

Before we dive into the details of the Forms API, we need a little scaffolding code to enable you to test the examples on a site. Here we map the URL example/page to the function call example_page(). We'll later fill this function with relevant code.

function example_menu($may_cache) {
  if ($may_cache) {
    $items = array();
    $items[] = array(
      'path' => 'example/page',
      'title' => 'Example',
      'callback' => 'example_page',
      'access' => user_access('access example'),
      'type' => MENU_CALLBACK,
    );
    return $items;
  }
}

function example_perm() {
  return array('access example');
}

/**
  * This function will be called upon a visit to example/page.
  */

function example_page() {
 
}

The example implements the following hooks:

hook_menu
Maps URLs to callback functions in addition to providing a way to define actual entries in the navigation menu.
hook_perm
Supplies permissions that can be assigned on the admin/users/access page and checked with user_access().

To use the example code on your site, you can save it in sites/all/modules/example/example.module together with example.info:

name = Example
description = Toy with FAPI examples.

FAPI stages

This is in progress.

The pivotal FAPI function is drupal_get_form(); it retrieves a form from a builder function, passes it on for processing, and renders the form or redirects to its destination as appropriate.

One can distinguish three stages in the life of a form:

  1. Stage I - Building.
  2. Stage II - Validation.
  3. Stage III - Submission.

Stage I - Building

The building stage starts when you call drupal_get_form('form_id'), with the function scurrying away to find the builder function of the appropriate form. To keep it simple for now; drupal_get_form checks if the function form_id() exists, then calls it, expecting a proper FAPI array in return. Alternatives to this approach will be discussed in future chapters.

In the code below, we fetch a form with the identifier myform, build by the function myform(), specifying a select box, a textfield and a button.

function example_page() {
// Forms are identified by their form id, in this case 'myform'.
// drupal_get_form will fetch the form from the builder function myform().
return drupal_get_form('myform');
}

/**
* Builder function for the form with the form_id myform.
* Returns a form array.
*/

function myform() {
$form = array();

// A combo box with three options.
$form['gender'] = array(
'#type' => 'select',
'#title' => t('Gender'),
'#options' => array('male' => t('Male'), 'female' => t('Female'), 'unknown' => t('Not sure')),
);

// A textfield to provide the number of siblings. We have to ensure numeric input.
$form['siblings'] = array(
'#type' => 'textfield',
'#title' => t('How many siblings do you have?'),
);

// Submission button with the text 'Save'.
$form['save'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}

This builder function supplies a number of fields defined by keys of the nested forms array. The '#key' => 'value' pairs are properties of the field. We'll deal with those later, but you can take a look at the FAPI reference to get a feel for the available properties.

Stage II - Validation

When the user clicks a button on the form, data is send back to the server. Drupal reruns the form builder function and then starts the validation stage. A few properties are automatically validated. Required fields, maximum input length (textfields), whether selected options are valid options and the internal form token (to protect against cross site request forgeries).

There are multiple ways to do some validation yourself, but the easiest is to provide a [form_id]_validate function. This takes two arguments: the form id and an array of values. In the example we use a custom validation function to make sure that the number of siblings is numeric. By setting an error on the field 'siblings' we ensure that the form is redisplayed to the user with the error message "Please enter a number".

/**
* Input validation takes place in [form_id]_validate().
*/

function myform_validate($form_id, $form_values) {
// If the input received in the siblings field is not numeric, set an error on that field.
// This will prevent progression to [form_id]_submit() and redisplay the form to the user.
if (!is_numeric($form_values['siblings'])) {
form_set_error('siblings', t('Please enter a number.'));
}
}

When the form validation is successful, the next stage starts: submission.

Stage III - Submission

/**
* With validation passed [form_id]_submit() acts on the submission.
*/

function myform_submit($form_id, $form_values) {
$query = "INSERT INTO {example} (gender, siblings) VALUES ('%s', %d)";
db_query($query, $form_values['gender'], $form_values['siblings']);
}