Initializing and Refreshing a Multi-Step Form
I've been working on a relatively complex multi-step form for a while now. It takes a URL argument that represents a set of stored options to control display and behavior. Thanks to $form_state['storage'] that is pretty easy to grab once and keep through all the steps.
However, I was having some occasional strange behavior with JS settings and page refreshes. Poking around a bit, it turns out that assigning values into storage outside of a submit handler isn't a great idea. That's good to know, since pretty much every example of multi-step forms I've seen makes a step assignment within the constructor.
My solution, outlined below, is to do a programmatic submit when the form first loads. Then we can grab the options and save them from within the submit handler.
This has a nice side-effect of allowing the form to detect browser refreshes. For this form, it just starts over on a hard refresh, but other behaviors should be possible.
<?php
// form constructor
// $option_id is a URL argument
function multi_step_form(&$form_state, $option_id = NULL) {
$form = array();
$step = $form_state['storage']['step'];
if ($step) {
switch ($step) {
// SNIP - build the various steps
}
}
else {
// we're just starting out. programmatically submit the form.
$form['option_id'] = array(
'#type' => 'hidden',
'#value' => check_plain($option_id),
);
$form_state['submitted'] = TRUE;
multi_step_form_submit($form, $form_state);
}
return $form;
// submit handler
function multi_step_form_submit($form, &$form_state) {
// use the resubmit detected in _multi_step_load_options to reset
if ($form_state['storage']['resubmitted']) {
$form_state['storage']['step'] = 'one';
$form_state['storage']['resubmitted'] = FALSE;
}
else {
switch ($form_state['storage']['step']) {
// SNIP - process flow depending on step
default:
// initialize if no step is set
// this is the result of the programmatic submit above
_multi_step_load_options($form['option_id']['#value'], $form_state);
break;
}
}
}
function _multi_step_load_options($option_id = NULL, &$form_state) {
// get the option set, or a default
$options = multi_step_options_load($option_id);
if (!$options) {
$options = multi_step_options_load(DEFAULT_OPTION_ID);
}
// store the options and start the workflow
$form_state['storage']['options'] = $options;
$form_state['storage']['step'] = 'one';
// since this gets programatically called on initial generation,
// it should never see a $_POST. if it does, we know the user
// refreshed the form. let's throw them back to the start
// in multi_step_form_submit()
if ($_POST) {
$form_state['storage']['resubmitted'] = TRUE;
}
}
?>


