20 APIs in 20 Days: The Forms API

Apr
08

This post is an entry in our 20 APIs in 20 Days series. Learn more about how best practices lead to sustainable development at www.trellon.com.

There's not much that has not been said about Drupal's Forms API, and it can be a challenging concept for developers who are used to working with other platforms. Any series talking about the underlying framework would be incomplete without some discussion of how it is used.

The Forms API in Drupal provides developers with a way to dynamically construct, modify and secure forms on your web sites. Instead of writing out the markup for individual form elements, developers define a form through an array that instructs Drupal on how to assemble the form and present it to users. The Forms API also tells Drupal how to validate form submissions and what to do with any data being received.

The real power of the API, however, lies in the fact that custom modules can modify forms as they are being generated. Developers can create new fields, provide additional validation conditions, and handle form submissions dynamically. This kind of extensibility is unique to Drupal, and represents a very different pattern for designing web applications.

Why is There a Forms API?

Forms are an integral part of the Internet that allow web sites to collect data from users. Having a platform that allows you to handle form submissions easily is a big deal, when you consider what has come before.

Way back in the early days of the Internet, form submissions were generally handled through CGI scripts. These scripts mostly used single threaded Perl distros and were awfully slow compared to today's technologies. Some of the major challenges developers, network administrators, and web site operators have had to wrestle with included modifying forms within obtuse bash scripts, dealing with threading issues on servers, validating the data that was being submitted and securing those forms to prevent abuse. A common way to take down a web site back then was to submit forms with more information than they were designed to handle, which would lead to memory issues, locked threads, segfaults and all other sorts of nastiness.

Every framework for developing web based applications has had to deal with this state of affairs. The Drupal communities approach is somewhat unique, in that it emphasizes flexibility at multiple levels. This includes:

  • The automatic generation of forms through array based prototypes.
  • The ability to dynamically modify form structures throguh custom modules.
  • The ability to validate form submissions.
  • The ability to provide handlers for processing form submissions.
  • The ability to modify the presentation and layout of form submissions.
  • The ability to dynamically redirect forms post-submission to specified content.

If you think about it, going through all those levels of processing is a lot to ask of a PHP application. Drupal also provides some caching features for forms, to prevent them from having to be generated from scratch with every page request. This helps improve performance, but can have some important implications for developers.

A Simple Example

Creating a form is a relatively simple process in Drupal. Every form is declared inside of a PHP function, as seen below:

<?php
function myform($form_state) {
 
$form['mytext'] = array(
   
'#type'        => 'textfield',
   
'#title'       => t('My Text'),
   
'#description' => t('Enter your text'),
   
'#required'    => TRUE,
  );
 
$form['submit'] = array(
   
'#type'  => 'submit',
   
'#value' => t('Submit'),
  );

  return $form;
}
?>

Now, as discussed before, every form in Drupal is a structured array of values. This array allows you to declare the type of form element that it is being used (such as a textfield, select list, radio buttons, etc.), whether or not a value for the form is required, the default value of the element and some other useful attributes. There are a lot of options for declaring form elements, a complete list of which can be found at the Forms API reference page on api.drupal.org.

In order to get the form to generate on a web page, all someone would need to do print the form through a simple command:

<?php
print drupal_get_form('myform');
?>

This code returns a simple form with a textfield and a submit button.

Validating and Handling the Submission

Once a form has been generated, Drupal needs to know what to do with the information that comes in through the form. There are 2 steps to handling form submissions: validation and submission.

Validation is a step in the form submission process where the data being received is checked for accuracy. Drupal will automatically check to see if required fields are populated. In a validation handler, developers can write code to make sure form values are consistent with the design of their application. For instance, if you are looking for a number in a certain field, this is where you make sure that you are getting a number. There are some special functions in Drupal that let you trigger an error and ask the user to provide the proper values.

Submission is a step in the form submission process where data is being manipulated. If a form cannot be validated, the data will never reach the submission stage. Developers use submit handlers to store data received through an online form and do any necessary processing.

Like everything else in the Forms API, validation and submission handlers are defined dynamically. The simplest way to create handlers with the same name as the form. Using the example from above, the myform_validate and myform_submit functions will automatically process all submissions from the simple form.

<?php
function myform_validate($form, &$form_state) {
 
// Make sure stuff is right here
 
if (strlen($form_state['values']['mytext']) < 80) {
   
form_set_error('mytext', t('The text you entered must be at least 80 characters long.'));
  }
}

function myform_submit($form, &$form_state) {
 
drupal_set_message($form_state['values']['mytext']);
}
?>

form_set_error sets an error on a specific form element. In this case we check to see that our text field mytext is at least 80 characters long, and if it isn't we set an error on the form. This is displayed to the user through a status message and have the form field itself highlighted in red so that the user knows exactly where the error ocurred.

The myform_validate and myform_submit are known as the default validation callback and the default submit callback respectively. Since these are the default functions that are used they can be changed or additional validation and submission fuctions can be added. Drupal has two arrays that are part of your form for just this purpose.

<?php
// Add a validation callback
$form['#validate'][] = 'my_new_validation_function';

// Add another submission callback
$form['#submit'][] = 'my_new_submission_function';
?>

These functions will be called in the order they are placed in the array and using them gives you the opportunity to give your module more sophisticated behaviors.

Theming Your Forms

Not only do we want our forms to functional, we want them to be attractive too! Thankfully Drupal provides the methodology to have your forms look sexy. The first thing we need to do is to let Drupal's theme layer know that there is something we want to theme. This is done with hook_theme.

<?php
function mymodule_theme() {
  return array(
   
'myform' => array(
     
'arguments' => array('element' => NULL),
    ),
  );
}
?>

This lets the theme layer know that we'll be using a theme function. Again, Drupal automatically generates a name for this function, in this case since we told the theme layer we were using myform the function is simply theme_myform. In this function we can use the function drupal_render to generate the HTML for our form elements.

<?php
function theme_myform(&$element) {
 
// A theme function for a form always has the form element itself
  // as the first parameter.  Now we can add custom markup or otherwise
  // change how the form is being displayed.
 
$output = '<h2>' . t('Our custimaztion to the theme') . '</h2>';
 
$output .= drupal_render($element['mytext']);
 
$output .= drupal_render($element['submit']);

  // This line will render any parts of the form that haven't been already.
  // Useful for hidden fields and a good step to make sure you didn't miss anything.
 
$output .= drupal_render($element);

  return $output;
}
?>

The only thing left to do then is to modify the form array to let it know about our new theme function.

<?php
$form
['#theme'] = 'myform';
?>

This is a technique that can be used at any level in a form array. So if you want to change how the entire form is presented, or just a single text box, Drupal makes it a straightforward task to accomplish.

AJAX Forms

Now that we’ve covered the basics we can start spicing things up. Let’s say we wanted to submit a form via AJAX. It’s just a matter of adding a bit of code to our existing form. So first things first, we had an #ahah property to our submit button (AHAH stands for Asynchronous HTML and HTTP). This lets Drupal know there should be an AJAX action when we click the button.

<?php
function myform_sweet($form_state) {
 
$form['mytext'] = array(
   
'#prefix'      => '<div id="wrapper">',
   
'#suffix'      => '</div>',
   
'#type'        => 'textfield',
   
'#title'       => t('My Text'),
   
'#description' => t('Enter your text'),
   
'#required'    => TRUE,
  );
 
 
$form['submit'] = array(
   
'#type'  => 'textfield',
   
'#value' => 'submit',
   
'#ahah'  => array(
     
'path'    => 'ajaxsubmit',
     
'wrapper' => 'wrapper'
      'method' 
=> 'replace',
     
'effect'  => 'fade',
    ),
  );

  return $form;
}
?>

You’ll notice one of the properties of the AHAH array is ‘path’. This means a Drupal internal path that will be accessed when the button is clicked so we set that up using Drupal’s hook_menu().

<?php
function mymodule_menu() {
 
$items['ajaxsubmit'] = array(
   
'page callback' => 'ajaxsubmit',
   
'access arguments' => array('access content'),
   
'type' => MENU_CALLBACK,
  );
}

function ajaxsubmit() {
 
// Set value in here 
 
drupal_set_message(t("Value set!"));
 
$messages = theme('status_messages'); 
  return
drupal_json('data' => $messages, 'status' => TRUE);
}
?>

And that’s all there is to it. There is a little more special sauce removed for the sake of expedience so you can check out more of the details here.

Wait, There’s More

The Forms API is one of the most discussed topics in Drupal, and it is beyond the scope of this article to discuss all the things you can do with it. Some topics you might want to explore:

Be sure to catch all of Trellon’s API blog posts as we explore the fundamental tools for extending Drupal’s functionality.

5 Comments

Thank you for these articles

Thank you for these articles - I am really enjoying them, and I think I'm even learning a few things along the way. (I'm a true drupal novice.)

Having said that, may I suggest changing "my_new_submission_funciton" to "my_new_submission_function"?

Thanks for the feedback Ken.

Thanks for the feedback Ken.

Another major answer to the

Another major answer to the question "Why is there a Form API?" is security. If you aren't using FAPI for forms then you take on the responsibility of checking things like:
- Did the submitter attempt to add any fields that weren't in the original form?
- For select fields and radios is the submitter attempting to submit a value that wasn't on the original form.
That's a whole lot of work that wasn't happening in the pre-FAPI days.

The Examples Module now has a

The Examples Module now has a complete set of live, working Form API examples. It also has D7 AJAX examples. Contributions and improvements are welcome.

Are you going to continue the

Are you going to continue the series? I'm really looking forward to new articles of yours :)

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options