Drupal 6: Creating a multipage form using the Forms API

Recently I've been struggling with creating a multi-step CCK node form in Drupal 5. I was able to implement a solution that used a combination of form_alter and jQuery, but due to the amount of jQuery, it was too slow. I then attempted the same functionality in Drupal 6, but due to the number of changes in the Forms API, I decided to take a step back. Here's my first successful attempt at creating a multi-step form using a Drupal 6 module and the Forms API. The following code adds the ability to jump to any step of the form, save at any point, and dynamically creates steps based on the number of fieldsets. My next step will be transposing this code to work with a CCK node form.

Screenshot:

<?phpfunctionmultipage_menu(){$items=array();$items['multiStepForm']=array('title'=>t('Multi Step Form'),'page callback'=>'drupal_get_form','page arguments'=>array('multipage_test_form'),'access arguments'=>array('access content'),);return$items;}functionmultipage_test_form(&$form_state){// enable/disable debugging output$form_state['storage']['#navigation']['debugging']=false;// per debuggingif($form_state['storage']['#navigation']['debugging'])echo"<HR>LOADING FUNCTION: multipage_test_form<HR>";// ensure step is set in storageif(empty($form_state['storage']['#navigation']['step'])){$form_state['storage']['#navigation']['step']=1;}elseif($form_state['storage']['#navigation']['newStep']){// if the form has been submitted, check for a new step$form_state['storage']['#navigation']['step']=$form_state['storage']['#navigation']['newStep'];unset($form_state['storage']['#navigation']['newStep']);}$form=array();// add form fields_multipage_test_form_add_fields($form,$form_state);// calculate max number of steps_multipage_test_form_max_steps($form,$form_state);// create tabs_multipage_test_form_create_tabs($form,$form_state);// remove form fields per step_multipage_test_form_remove_fields($form,$form_state);// add tabs to form_multipage_test_form_add_tabs($form,$form_state);// add buttons to form_multipage_test_form_add_buttons($form,$form_state);return$form;}function_multipage_test_form_add_fields(&$form,&$form_state){// per debuggingif($form_state['storage']['#navigation']['debugging'])echo"<hr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ADDING FIELDS<hr>";// Fieldset A$form['fieldset_a']=array('#type'=>'fieldset','#title'=>t('Fieldset A'),);$form['fieldset_a']['textfield_a1']=array('#type'=>'textfield','#title'=>t('Textfield A1'),'#default_value'=>$form_state['storage']['textfield_a1'],);$form['fieldset_a']['textfield_a2']=array('#type'=>'textfield','#title'=>t('Textfield A2'),'#default_value'=>$form_state['storage']['textfield_a2'],);$form['fieldset_a']['textfield_a3']=array('#type'=>'textfield','#title'=>t('Textfield A3'),'#default_value'=>$form_state['storage']['textfield_a3'],);// Fieldset B$form['fieldset_b']=array('#type'=>'fieldset','#title'=>t('Fieldset B'),);$form['fieldset_b']['textfield_b1']=array('#type'=>'textfield','#title'=>t('Textfield B1'),'#default_value'=>$form_state['storage']['textfield_b1'],);$form['fieldset_b']['textfield_b2']=array('#type'=>'textfield','#title'=>t('Textfield B2'),'#default_value'=>$form_state['storage']['textfield_b2'],);$form['fieldset_b']['textfield_b3']=array('#type'=>'textfield','#title'=>t('Textfield B3'),'#default_value'=>$form_state['storage']['textfield_b3'],);// Fieldset C$form['fieldset_c']=array('#type'=>'fieldset','#title'=>t('Fieldset C'),);$form['fieldset_c']['textfield_c1']=array('#type'=>'textfield','#title'=>t('Textfield C1'),'#default_value'=>$form_state['storage']['textfield_c1'],);$form['fieldset_c']['textfield_c2']=array('#type'=>'textfield','#title'=>t('Textfield C2'),'#default_value'=>$form_state['storage']['textfield_c2'],);$form['fieldset_c']['textfield_c3']=array('#type'=>'textfield','#title'=>t('Textfield C3'),'#default_value'=>$form_state['storage']['textfield_c3'],);// Fields not in a fieldset$form['textfield_d1']=array('#type'=>'textfield','#title'=>t('Textfield D1'),'#default_value'=>$form_state['storage']['textfield_d1'],);$form['textfield_d2']=array('#type'=>'textfield','#title'=>t('Textfield D2'),'#default_value'=>$form_state['storage']['textfield_d2'],);$form['textfield_d3']=array('#type'=>'textfield','#title'=>t('Textfield D3'),'#default_value'=>$form_state['storage']['textfield_d3'],);}function_multipage_test_form_max_steps($form,&$form_state){// calculate max stpes if emptyif(empty($form_state['storage']['#navigation']['maxSteps'])){$fieldsetCount=0;$otherFields=false;// loop through form elementsforeach($formas$k=>$v){// check for fieldsetif(substr($k,0,9)=='fieldset_'&&is_array($v)&&$v['#type']=='fieldset'){$fieldsetCount++;}else{$otherFields=true;}}// if there are other fields, increment max stepsif($otherFields){$fieldsetCount++;$form_state['storage']['#navigation']['otherFields']=true;}// add count to storage$form_state['storage']['#navigation']['maxSteps']=$fieldsetCount;}}function_multipage_test_form_create_tabs(&$form,&$form_state){// per debuggingif($form_state['storage']['#navigation']['debugging'])echo"<hr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CREATING TABS<hr>";// create list of tabs if not set in storageif(empty($form_state['storage']['#navigation']['tabs'])){$tabCount=0;$tabs=array();// check for other fieldsif($form_state['storage']['#navigation']['otherFields']){$tabsCount++;$tabs[$tabsCount]="Start";}// loop through form itemsforeach($formas$k=>$v){if(substr($k,0,9)=='fieldset_'&&is_array($v)&&$v['#type']=='fieldset'){$tabsCount++;$tabs[$tabsCount]=$v['#title'];}}// add tabs to storage$form_state['storage']['#navigation']['tabs']=$tabs;}}function_multipage_test_form_remove_fields(&$form,&$form_state){// per debuggingif($form_state['storage']['#navigation']['debugging'])echo"<HR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;REMOVING FIELDS<HR>";// set fieldset counter based on otherfieldsif($form_state['storage']['#navigation']['otherFields']){$fieldsetCount=1;}else{$fieldsetCount=0;}// loop though form fieldsforeach($formas$k=>$v){// ensure this form item is a fieldsetif(substr($k,0,9)=='fieldset_'&&is_array($v)&&$v['#type']=='fieldset'){// increment count of fieldsets$fieldsetCount++;// unset fieldsetif($form_state['storage']['#navigation']['step']!=$fieldsetCount)unset($form[$k]);}elseif(is_array($v)&&$form_state['storage']['#navigation']['otherFields']&&$form_state['storage']['#navigation']['step']>1){// unset fieldunset($form[$k]);}}}function_multipage_test_form_add_tabs(&$form,$form_state){// per debuggingif($form_state['storage']['#navigation']['debugging'])echo"<HR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ADDING TABS<HR>";$tabsArray=array();$tabsCount=0;// loop through tabs and create buttons for each oneforeach($form_state['storage']['#navigation']['tabs']as$k=>$v){$tabsCount++;$tabsArray['tab_'.$k]=array('#type'=>'submit','#value'=>t($v),'#attributes'=>array('class'=>'tabs'.($tabsCount==$form_state['storage']['#navigation']['step']?' active':'')),);}// add tab buttons to beginning of form$form=array_merge($tabsArray,$form);}function_multipage_test_form_add_buttons(&$form,&$form_state){// per debuggingif($form_state['storage']['#navigation']['debugging'])echo"<HR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ADDING BUTTONS<HR>";// add previous buttonif($form_state['storage']['#navigation']['step']>1){$form['previous']=array('#type'=>'submit','#value'=>t('Previous'),);}// add next buttonif($form_state['storage']['#navigation']['step']<$form_state['storage']['#navigation']['maxSteps']){$form['next']=array('#type'=>'submit','#value'=>t('Next'),);}// add save button$form['submit']=array('#type'=>'submit','#value'=>t('Save'),);}functionmultipage_test_form_validate($form,&$form_state){// per debuggingif($form_state['storage']['#navigation']['debugging'])echo"<hr>FORM VALIDATED<hr>";}functionmultipage_test_form_submit($form,&$form_state){// per debuggingif($form_state['storage']['#navigation']['debugging'])echo"<hr>FORM SUBMITTED<hr>";// loop through submitted form valuesforeach($form_state['values']as$k=>$v){// add certain fields to storageif(substr($k,0,10)=='textfield_'){$form_state['storage'][$k]=$v;}}// handle button actionswitch($form_state['clicked_button']['#value']){case'Previous':// set the state to rebuild$form_state['rebuild']=TRUE;// set new stepif($form_state['storage']['#navigation']['step']>1){$form_state['storage']['#navigation']['newStep']=$form_state['storage']['#navigation']['step']-1;}break;case'Next':// set the state to rebuild$form_state['rebuild']=TRUE;// set new stepif($form_state['storage']['#navigation']['step']<$form_state['storage']['#navigation']['maxSteps']){$form_state['storage']['#navigation']['newStep']=$form_state['storage']['#navigation']['step']+1;}break;case'Save':// TODObreak;// tabsdefault:// check if the button value maps to a tab$key=array_search($form_state['clicked_button']['#value'],$form_state['storage']['#navigation']['tabs']);if($key){$form_state['storage']['#navigation']['newStep']=$key;}break;}}?>