Basic Drupal 6 multipart form example with "Previous" and "Next" buttons

An example of a basic multistep Drupal 6 form with 3 steps, "Previous" and "Next" buttons on each step and possibility of moving both forwards and backwards in the flow with submitted data being passed on between all steps

Here is an example of a basic multistep Drupal 6 form with 3 steps, "Previous" and "Next" buttons on each step and possibility of moving both forwards and backwards in the flow with submitted data being passed on between all steps.

/**
 * Defines total number of form steps.
 */
define('TEST_FORM_TOTAL_STEPS', 3);

/**
 * Implements hook_menu().
 */
function test_menu() {
  $items = array();
  // Form page.
  $items['test/form'] = array(
    'title' => t('Test Form'),
    'page callback' => 'test_page',
    'page arguments' => array(),
    'access arguments' => array('access content'),
  );
  // Success page.
  $items['test/success'] = array(
    'title' => t('Test successful!'),
    'page callback' => 'test_success',
    'page arguments' => array(),
    'access arguments' => array('access content'),
  );
  return $items;
}

/**
 * Fetches and displays the form.
 */
function test_page() {
  return drupal_get_form('test_form');
}

/**
 * Displays simple confirmation page after finishing the whole process.
 */
function test_success() {
  return 'OK!';
}

/**
 * Generates the form.
 */
function test_form($form_state) {

  // Set current step to 1 if user just starts the process.
  if (!isset($form_state['storage']['step'])) {
    $form_state['storage']['step'] = 1;
  }

  switch ($form_state['storage']['step']) {

    case 1:

      // Custom page title.
      drupal_set_title('Step 1');

      // All the real form fields.
      $form['step1_field1'] = array(
        '#title' => 'Step 1 Field 1',
        '#type' => 'textfield',
        '#default_value' => $form_state['storage']['values'][1]['step1_field1'],
      );

      // Add submit button(s).
      $form += _test_add_submit_buttons($form_state);

      // Not important - used only for displaying values posted so far.
      $form['posted_values'] = array(
        '#type' => 'markup',
        '#value' => _test_display_posted_values($form_state),
      );
      break;

    case 2:

      // Custom page title.
      drupal_set_title('Step 2');

      // All the real form fields.
      $form['step2_field1'] = array(
        '#title' => 'Step 2 Field 1',
        '#type' => 'textfield',
        '#default_value' => $form_state['storage']['values'][2]['step2_field1'],
      );

      // Add submit button(s).
      $form += _test_add_submit_buttons($form_state);

      // Not important - used only for displaying values posted so far.
      $form['posted_values'] = array(
        '#type' => 'markup',
        '#value' => _test_display_posted_values($form_state),
      );
      break;

    case 3:

      // Custom page title.
      drupal_set_title('Step 3');

      // All the real form fields.
      $form['step3_field1'] = array(
        '#title' => 'Step 3 Field 1',
        '#type' => 'textfield',
        '#default_value' => $form_state['storage']['values'][3]['step3_field1'],
      );

      // Add submit button(s).
      $form += _test_add_submit_buttons($form_state);

      // Not important - used only for displaying values posted so far.
      $form['posted_values'] = array(
        '#type' => 'markup',
        '#value' => _test_display_posted_values($form_state),
      );
      break;
  }
  return $form;
}

/**
 * Handles submitted form.
 */
function test_form_submit($form, &$form_state) {

  // Save all posted values for $form_state['storage']
  // to make them available in all other steps.
  $form_state['storage']['values'][$form_state['storage']['step']] = $form_state['values'];

  // Deal with current step based on which submit button was clicked.
  switch ($form_state['clicked_button']['#id']) {

    case 'edit-next':
      // Do not finally submit the form yet, rebuild it instead.
      $form_state['rebuild'] = TRUE;
      // Go to next step.
      $form_state['storage']['step']++;
      break;

    case 'edit-prev':
      // Do not finally submit the form yet, rebuild it instead.
      $form_state['rebuild'] = TRUE;
      // Go to previous step.
      $form_state['storage']['step']--;
      break;

    case 'edit-finish':
      // Do something with all form values ($form_state['storage']) before unsetting it.
      // ...
      // And then unset it.
      unset($form_state['storage']);
      // This was the final step, so redirect to success page.
      $form_state['redirect'] = 'test/success';
      break;
  }
}

/**
 * Adds relevant submit buttons based on current step.
 */
function _test_add_submit_buttons($form_state) {
  $buttons = array();
  // Display 'Prev' button on all steps excluding first.
  if ($form_state['storage']['step'] > 1) {
    $buttons['prev'] = array(
      '#type' => 'submit',
      '#value' => 'Prev',
    );
  }
  // Display 'Next' button on all steps excluding last.
  if ($form_state['storage']['step'] < TEST_FORM_TOTAL_STEPS) {
    $buttons['next'] = array(
      '#type' => 'submit',
      '#value' => 'Next',
    );
  }
  // Display 'Finish' button on the last step.
  if ($form_state['storage']['step'] == TEST_FORM_TOTAL_STEPS) {
    $buttons['finish'] = array(
      '#type' => 'submit',
      '#value' => 'Finish',
    );
  }
  return $buttons;
}

/**
 * Displays values posted so far beneath the form.
 */
function _test_display_posted_values($form_state) {
  $output  = '<p>Posted values:</p>';
  $output .= '<p>Step 1 field 1: ' . $form_state['storage']['values'][1]['step1_field1'] . '</p>';
  $output .= '<p>Step 2 field 1: ' . $form_state['storage']['values'][2]['step2_field1'] . '</p>';
  $output .= '<p>Step 3 field 1: ' . $form_state['storage']['values'][3]['step3_field1'] . '</p>';
  return $output;
}