Community & Support

Need help understanding D6's multistep form replacement

#multistep is no longer valid in Drupal 6, but I can't make heads or tails of what is shown in the upgrade doc:

<?php
function my_form($form_state, $param1) {
 
// standard form definition code goes here...

 
if (!empty($form_state)) {
   
$submitted_data = $form_state['values'];
   
$my_custom_stuff = $form_state['my_data'];

   
// We're building the form a second time based on previous input...
    // Add additional fields, etc.
 
}
}

function
my_form_submit($form, &$form_state) {
 
// process the form input...
 
$form_state['rebuild'] = TRUE;
 
$form_state['my_data'] =  'this will be passed back to the form constructor when the form is rebuilt...';
}
?>

There's just too little there for me to figure out what it should be. Here's my old form stripped down to a bare framework so you can see what I'm trying to do:

<?php
function letters_form($form_values = NULL){
 
$form = array();

 
$form['#multistep'] = TRUE;
 
$form['#redirect'] = FALSE;

 
$step = (!isset($form_values)) ? $form_values['step'] + 1 : 1;
 
 
$form['step'] = array(
   
'#type' => 'hidden',
   
'#value' => $step,
  );

  switch (
$step) {
    case
1:
     
// first step fields
     
break;
    case
2:
     
// do something with first step values
      // present second step fields
     
break;
    case
3:
     
// do something with second step values
      // present third step fields
     
break;
  }

  return
$form;
}

function
letters_form_validate($form_id,$form_values){
  if (
$form_values['step'] == 1) {
   
// validation for the first step
 
}
  if (
$form_values['step'] == 2) {
   
// validation for the second step
 
}
  if (
$form_values['step'] == 3) {
   
// validation for the third step
 
}
}

function
letters_form_submit($form_values){
  if (
$form_values['step'] == 3) {
   
// final form submission
   
return 'some/path';
  }
}
?>

It just goes through three steps and does something after the third submission. The problem I'm having a hell of a time wrapping my head around the code in the docs and there are no examples I can find that are detailed enough to get a sense of how it works but not so complex as to be totally overwhelming. I'm also having a very hard time finding any examples that aren't node related.

Comments

No one else gets this stuff

Example

You need to change the callback on the submit button. You need just two functions to do this:

<?php
/**
* Implementation of hook_form_$form-id_alter().
*/
function node_example_form_node_example_node_form_alter(&$form, $form_state) {
 
$form['buttons']['submit']['#submit'] = array('_node_example_form_submit');
}

/**
* Rebuild the form if something has changed.
* @see node_example_form()
*/
function _node_example_form_submit($form, &$form_state) {
  if (
$form['#node']->node_example != $form_state['values']['node_example']) {
   
$form_state['rebuild'] = TRUE;
  }
  else {
   
node_form_submit($form, $form_state);
  }
}
?>

Need help as well

Hi Darren, maybe you could be so kind to help me as well. I have a similar problem. I'm trying to port a module, but have no coding experience. Using the coder module it mention #multistep has been replaced by $form_state. How would that look like in the following function? I'd appreciate any input.

<?php
function resizer_user($op, &$edit, &$account, $category = NULL) {
   
    if (
$op == 'form' && $category == 'avatar') {
       
$pic = $_POST['tmp_picture'];
       
$picname = $_POST['tmp_picture_name'];
       
$newpic = $_POST['newImage'];
        if(isset(
$_POST['step'])) {
           
$step = $_POST['step'] + 1;
        } else {
           
$step = 1;
        }
       
$form['avatar'] = array(
           
'#type' => 'fieldset',
        );

       
$form['avatar']['step'] = array(
           
'#type' => 'hidden',
           
'#value' => $step,
        );
       
       
$form['#multistep'] = TRUE;
       
$form['#redirect'] = FALSE;
       
$form['#attributes'] = array('enctype' => "multipart/form-data");
       
        switch(
$step){
            case
1:
               
$form['avatar']['preview'] = array(
                 
'#type' => 'markup',
                 
'#value' => theme('user_picture', $account),
                 
'#prefix' => '<h2>'.t('Your current picture').'</h2>',
                );
               
$form['avatar']['picture_upload'] = array(
                 
'#type' => 'file',
                 
'#title' => t('Upload a new picture'),
                 
'#size' => 20,
                );
                break;
            case
2:
               
$here = drupal_get_path('module','resizer');
               
                list(
$width, $height) = explode('x', variable_get('user_picture_dimensions', '85x85'));
               
               
$js = "width = $width;" .
                       
"height = $height;" .
                       
"startCropper(width,height);" ;

               
drupal_add_js($here.'/cropper/lib/prototype.js');
               
drupal_add_js($here.'/cropper/lib/scriptaculous.js?load=builder,dragdrop');
               
drupal_add_js($here.'/cropper/cropper.js');
               
drupal_add_js($js,'inline');
               
drupal_add_js($here.'/resizer.js','theme');
               
drupal_add_css($here.'/resizer.css');
               
               
$form['avatar']['picpic'] = array(
                   
'#type' => 'markup',
                   
'#value' => "<img id='testImage' src='".base_path().$pic."' />"
               
);
               
$form['avatar']['tmp_picture'] = array(
                   
'#type' => 'hidden',
                   
'#value' => $pic
               
);
               
$form['avatar']['tmp_picture_name'] = array(
                   
'#type' => 'hidden',
                   
'#value' => $picname
               
);
               
$form['avatar']['x1'] = array(
                   
'#type' => 'hidden',
                   
'#value' => ''
               
);
               
$form['avatar']['y1'] = array(
                   
'#type' => 'hidden',
                   
'#value' => ''
               
);
               
$form['avatar']['x2'] = array(
                   
'#type' => 'hidden',
                   
'#value' => ''
               
);
               
$form['avatar']['y2'] = array(
                   
'#type' => 'hidden',
                   
'#value' => ''
               
);
               
$form['avatar']['width'] = array(
                   
'#type' => 'hidden',
                   
'#value' => ''
               
);
               
$form['avatar']['height'] = array(
                   
'#type' => 'hidden',
                   
'#value' => ''
               
);
               
$form['avatar']['previewArea'] = array(
                   
'#type' => 'markup',
                   
'#value' => "Preview: <div id='previewArea'></div>"
               
);
               
                break;
            case
3:
               
$form['#redirect'] = array(
                   
'user',
                   
'destnation=node'
               
);               
        }   

        return
$form;
    }
    if (
$op == 'validate' && $category == 'avatar') {
       
$step = $_POST['step'];
        if (
$file = file_check_upload('picture_upload')) {
           
resizer_validate_user_picture($file, $edit, $account, $form, $step);
        }
    }
    if(
$op == 'submit' && $category == 'avatar') {
       
$step = $_POST['step'];
       
resizer_submit_user_picture($edit, $account, $form, $step);
    }
}
?>

This is what to do

There are some variants for how to make it work. Mine is to use the _validate function to check whether we should submit the form or to go to the next step. Like this (check the comments I added too):

<?php
function letters_form($form_state){
 
// For the sake of easily maintaining the module in different core versions,
  // create our own $form_values variable.
 
$form_values = $form_state['values'];
 
$form = array();

 
// Get rid of these, they're not needed any more.
  //$form['#multistep'] = TRUE;
  //$form['#redirect'] = FALSE;

 
$step = (!isset($form_values)) ? $form_values['step'] + 1 : 1;

 
$form['step'] = array(
   
'#type' => 'hidden',
   
'#value' => $step,
  );

  switch (
$step) {
    case
1:
     
// first step fields
     
break;
    case
2:
     
// do something with first step values
      // present second step fields
     
break;
    case
3:
     
// do something with second step values
      // present third step fields
     
break;
  }

  return
$form;
}

function
letters_form_validate($form, &$form_state){
 
$form_values = $form_state['values'];
  if (
$form_values['step'] == 1) {
   
// validation for the first step
    // Setting $form_state['rebuild'] = TRUE will prevent the submit function from
    // being processed, and the form is rebuilt. All the values from the previous
    // step remain in $form_state['values'].
   
$form_state['rebuild'] = TRUE;
  }
  if (
$form_values['step'] == 2) {
   
// validation for the second step
   
$form_state['rebuild'] = TRUE;
  }
  if (
$form_values['step'] == 3) {
   
// validation for the third step
   
$form_state['rebuild'] = FALSE;
  }
}

function
letters_form_submit($form, &$form_state){
 
$form_values = $form_state['values'];
 
// Checking the $step is not necessary any more, since processing will never reach
  // the submit function while $form_state['rebuild'] = TRUE.
  //if ($form_values['step'] == 3) {
    // final form submission
   
return 'some/path';
 
//}
}
?>

As for the code mariusrooms posted, I don't get how that works. Somehow it seems to combine the form and its validation and submit functions into a single function. That doesn't follow any Forms API version I know, and therefore I don't know how to rework it either.

User Picture Resizer

The code I posted comes from this module: http://drupal.org/project/resizer I was hoping with my non-experience to port it to 6 since it seems no one else will and I really would like to continue using it with Drupal 6. The module seems not to complicated. It could also use some updating to the jquery library as mentioned in one the active tickets there. If anyone has experience porting and is looking for a project, this would be a good one!

Any help would be great.

Regards,

Marius

nobody click here