Multi-page Forms in Drupal 6

Sun, Apr 4, 2010 - 11:14am -- Isaac Sukin

I recently did some work for a client that involved multi-page forms. Specifically, the client wanted a page where users could choose one of five forms, each of which had about ten steps with one question each. Users had to be able to move forward and backwards through the forms, with their data saved when they finished.

I knew a lot about working with forms in Drupal, although I'd never actually worked that extensively with multi-paged forms. But I did my research, and luckily there are some nice tutorials out there. However, most of these were specifically for forms with only two pages, or covered older versions of Drupal, or were crazy complicated. There is a great tutorial at pingVision which I unfortunately found after the fact, but this also doesn't cover a "back" button.

So, here's an example of what I came up with. It's a little clumsy, and there's probably room for simplification, but it works. You can move back and forward in the form and all of your data stays inputted without getting mysteriously wiped away.

/**
 * This is our form callback.
 * Display using drupal_get_form('work_survey');
 */
function work_survey(&$form_state) {
  // $form_state['storage']['step'] keeps track of what page we're on.
  if (!isset($form_state['storage']['step'])) {
    $form_state['storage']['step'] = 1;
  }
 
  //Don't lose our old data when returning to a page with data already typed in.
  $default_value = '';
  if (isset($form_state['storage']['values'][$form_state['storage']['step']])) {
    $default_value = $form_state['storage']['values'][$form_state['storage']['step']];
  }
 
  switch ($form_state['storage']['step']) {
    case 1:
      $form['current-work-env'] = array(
        '#type' => 'textarea',
        '#title' => t('Describe your current work environment.'),
        '#default_value' => isset($default_value['current-work-env']) ? $default_value['current-work-env'] : '',
      );
      $form['ideal-work-env'] = array(
        '#type' => 'textarea',
        '#title' => t('Describe your ideal work environment.'),
        '#default_value' => isset($default_value['ideal-work-env']) ? $default_value['ideal-work-env'] : '',
      );
      break;
    case 2:
      $form['country'] = array(
        '#type' => 'textfield',
        '#title' => t('In what country do you live?'),
        '#default_value' => isset($default_value['country']) ? $default_value['country'] : '',
      );
      $form['state'] = array(
        '#type' => 'textfield',
        '#title' => t('In what region or province do you live?'),
        '#default_value' => isset($default_value['state']) ? $default_value['state'] : '',
      );
      break;
    case 3:
      $form['current-industry'] = array(
        '#type' => 'textfield',
        '#title' => t('In what industry do you currently work?'),
        '#default_value' => isset($default_value['current-industry']) ? $default_value['current-industry'] : '',
      );
      $form['ideal-industry'] = array(
        '#type' => 'textfield',
        '#title' => t('In what industry would you ideally like to work?'),
        '#default_value' => isset($default_value['ideal-industry']) ? $default_value['ideal-industry'] : '',
      );
      break;
    case 4:
      $form['thanks'] = array(
        '#value' => '<p>'. t('Thank you for your participation in this survey.') .'</p>',
      );
      break;
  }
 
  //Depending on what page we're on, show the appropriate buttons.
  if ($form_state['storage']['step'] > 1) {
    $form['previous'] = array(
      '#type' => 'submit',
      '#value' => t('<< Previous'),
    );
  }
  if ($form_state['storage']['step'] != 4) {
    $form['next'] = array(
      '#type' => 'submit',
      '#value' => t('Continue >>'),
    );
  }
  else {
    $form['finish'] = array(
      '#type' => 'submit',
      '#value' => t('Finish'),
    );
  }
  return $form;
}
 
/**
 * The submit callback for the work_survey form.
 */
function work_survey_submit($form, &$form_state) {
  //Save the values for the current step into the storage array.
  $form_state['storage']['values'][$form_state['storage']['step']] = $form_state['values'];
 
  //Check the button that was clicked and change the step.
  if ($form_state['clicked_button']['#id'] == 'edit-previous') {
    $form_state['storage']['step']--;
  }
  elseif ($form_state['clicked_button']['#id'] == 'edit-next') {
    $form_state['storage']['step']++;
  }
  elseif ($form_state['clicked_button']['#id'] == 'edit-finish') {
    //You should store the values from the form in the database here.
 
    //We must do this or the form will rebuild instead of refreshing.
    unset($form_state['storage']);
 
    //Go to this page after completing the form.
    $form_state['redirect'] = 'survey/work/thank-you';
  }
}

The big trick here is that if $form_state['storage'] is set, the form will rebuild itself after clicking one of the submit buttons instead of refreshing itself and going back to page 1.

Hopefully this helps someone in the future!