The Drupal FormAPI Rapid Development Quickstart

Ryan Weal

June 16, 2010

This article is based on a presentation I gave to help a friend who is new to FormsAPI. It will introduce you to a few concepts using Drupal's command line tools.
For the better part of the past year I have been working with Drupal's FormAPI rather intensely. The projects have now spanned a number of different applications. Some interacting with the user system, some with sessions and others with the database.
FormAPI can be notoriously cryptic for developers new to Drupal but once mastered they are a dream to work with.
Surfing online documentation was difficult for mastring this. Much of my knowledge did not click until I read Pro Drupal Development published by Apress.
Today's Project
We need to build a simple webform using Drupal's FormsAPI. It will take an action based on the user's selection and send them to another page when complete.
It could be done with webform or CCK, but we want to do this faster than with those tools... in code!
Our Assumptions
I prefer working at the command line, so that's all we're going to cover here. I'm assuming you're at your shell now and that Drush is installed.
Let's Prototype Faster
Make sure you're working in a site that isn't live... your scripting could bring the site down otherwise.
Then get over to your website's filesystem and begin:
drush dl module_builder
drush pm-enable module_builder
drush mbdl
Woah! What just happened? You just downloaded and installed the module builder and then ran it from Drush to get it to fetch all the known hooks that you can tap into with your programming. The command "mbdl" is an extention to Drush that came with module_builder.
Before we begin coding the module we should know what hooks we want to use/modify with the Drupal framework.
drush hooks
You might want to use more or less to scroll through that list if it is too big:
drush hooks|less
If you are new to this you probably don't have a clue of what you need. I'll give you a list of starter options in a second, but first let's see what happens when we choose just one of the hooks:
drush mb my_custom_module menu
You just asked module builder to prototype a new module called my_custom_module and to add Drupal's init hook. Init runs on every page so if you need to do some processing on every single page this is the way to do it.
The result should quickly scroll onto your screen:
Proposed my_custom_module.module:
// $Id$

 * @file my_custom_module.module
 * TODO: Enter file description here.

 * Implementation of hook_init().

function my_custom_module_init() {
That should be enough to get you doing Drupal-style code rather than putting random PHP everywhere.
Usually I also need Drupal's "menu" hook, it notifies the system of any pages I am creating. It isn't necessary for what we are doing but I plan on extending the module later so I put it in anyway. I also like to include the "perm" hook and add my own permissions settings.
We're ready to build the prototype now.
drush mb my_custom_module init menu perm --write
Note that last parameter. It creates the module folder in (probably) your sites/all/modules folder, complete with a pre-crafted .module and .info file.
I commented out menu in the my_custom_module.module file until I have a page function ready to go. I use init for some debugging on occasion so I'm keeping that in the mix. Permissions will be rolled out later but we already know who needs access so we put the control into the module early. We will implement the settings function at a later date.
function my_custom_module_perm() {
  return array('manage my custom module stuff');
This adds an entry to our permissions page. Note: we are not implementing anything that refers on it in this article.
We're ready to create a form now.
First create some functions. As of this writing module_builder doesn't seem to have any draft FormAPI forms for us to use and the form hook has the wrong parameters (they apply to nodes, I just want a form!).
function my_custom_module_form($form_state) {
  return $form;

function my_custom_module_form_validate($form, &$form_state) {

function my_custom_module_form_submit($form, &$form_state) {
That should be enough to get us started.
Now we just need to put something into each function to make our form. I have consulted the Drupal FormAPI Reference to learn that "radios" are the type of form field I want. That page also lists a bunch of options that apply to the "radios" type.
Let's define the form function with a single field with two radio buttons:
function my_custom_module_form($form_state) {
  $form['choices'] = array(
    '#title' => t('Choose the item that is right for you'),
    '#type' => 'radios',
    '#options' => array('1' => 'Product 1', '2' => 'Product 2'),
    '#required' => TRUE,
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Seriously submit!',

  return $form;
That "#options" parameter contains all of the options that will make up our radio button list. When processing, selecting "Product 1" will appear to us as simply "1" on our end to save us some typing in the processing stages.
I left the validation function blank in my rapid development project. I usually use it with multi-page forms where I need to store data between pages. It is also a great place to do error checking. Is that phone number numeric? Yeah, you'd check that here.
The submit function has a few neat tricks in my final version:
function my_custom_module_form_submit($form, &$form_state) {
  if($form_state['values']['choices'] == '1') {
    uc_cart_add_item('5713', 1, NULL, NULL, FALSE);
  if($form_state['values']['choices'] == '2') {
    uc_cart_add_item('5686', 1, NULL, NULL, FALSE);

  $form_state['redirect'] = 'cart/checkout';
You Übercart junkies will appreciate this one. If you picked "Product 1" you will get the site's product (nid) #5713 added to your shopping cart. It adds 1 item without any messaging.
If you pick "Product 2" you will get item #5686 added to your cart.
Those product numbers are the node ID (nid) for each product. If you aren't sure number to use to add to your cart, go find the product on the site and click edit. See the number in the URL? That's the node id.
Once it's in your cart the form sends you to the checkout page by setting the form redirect.
Last Step - Use the Form
You have to put the form somewhere. For this demonstration I used a "page" template and changed the input format to PHP to run it. You could also put it in a block using the PHP input filter, or alternatively just add an entry in the "menu" function we talked about earlier.
print drupal_get_form('my_custom_module_form');