0

The documentation says to put $form_state['redirect'] in your code when you want to send the form to a new page. However, when I use it in the third page submit handler of my multi-page form it doesn't work. Here's my ENTIRE code below...see near the bottom for the third page submit handler and the redirect function it calls.

Note: I NEED $form_state['rebuild'] set to TRUE and $form_state['storage']['step'] to call the function (otherwise it breaks my script). For example, if I put $form_state['rebuild'] to FALSE and remove the $form_state['storage']['step'] line in the else statement and replace it with the $form_state['redirect'] line (currently in the non-working function below the submit handler, then it redirects just fine...HOWEVER, it then breaks the ability of the form to go BACK...and in such case if you go BACK from the third page it takes you all the way back to the first page again and the form no longer remembers your answers. But I also need to redirect users and send them to a processing page to display database results...what do I do?

<?php

function pathfindertool_menu() {
  $items['pathfinder'] = array(
    'type' => MENU_CALLBACK,
    'title' => 'Pathfinder Tool',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('pathfindertool_form'),
    'access callback' => TRUE,
  );
  $items['pathfinder/recommendations'] = array
  (
    'title' => 'Recommendations',
    'page callback' => 'pathfindertool_recommendations_page',
    'access callback' => TRUE,
  );
  return $items;
}

// create wrapper form and submit handler
function pathfindertool_form($form, &$form_state) {
      // Check to see if anything has been stored.
      if ($form_state['rebuild']) {
        $form_state['input'] = array();
      }
      if (empty($form_state['storage'])) {
        // No step has been set so start with the first.
        $form_state['storage'] = array(
          'step' => 'pathfindertool_first_form',
        );
      }

      // Return the current form
      $function = $form_state['storage']['step'];
      $form = $function($form, $form_state);
      return $form;
    }

    /**
     * Submit handler for the multiform wrapper.
     *
     * @see pathfindertool_primary_form().
     */

    function pathfindertool_form_submit($form, &$form_state) {
      $values = $form_state['values'];
      if (isset($values['back']) && $values['op'] == $values['back']) {
        // Moving back in form.
        $step = $form_state['storage']['step'];
        // Call current step submit handler if it exists to unset step form data.
        if (function_exists($step . '_submit')) {
          $function = $step . '_submit';
          $function($form, $form_state);
        }
        // Remove the last saved step so we use it next.
        $last_step = array_pop($form_state['storage']['steps']);
        $form_state['storage']['step'] = $last_step;
      }
      else {
        // Record step.
        $step = $form_state['storage']['step'];
        $form_state['storage']['steps'][] = $step;
        // Call step submit handler if it exists.
        if (function_exists($step . '_submit')) {
          $function = $step . '_submit';
          $function($form, $form_state);
        }
      }
      return;  
    }

    /**
     * First of three.
     */

    function pathfindertool_first_form($form, $form_state) {
      if(!empty($form_state['storage']))
        $values = $form_state['storage'];
      $form['level'] = array(
        '#prefix' => '<div class="question-intro-text">[WE NEED AN INTRO PAGE] To get your recommendations we need to know three things: your experience level, your role, and the topics that interest you. Begin by letting us know how you consider your experience level in your field.</div>',
        '#required' => TRUE,
         '#type' => 'radios',
         '#title' => t('What is your experience level? (pick one)'),
        '#default_value' => isset($values['level']) ? $values['level'] : NULL,
        '#options' => array(
        t('Novice'),
        t('Intermediate'),
        t('Advanced'))
    );
      $form['submit'] = array(
        '#type' => 'submit',
        '#value' => t('Next'),
      );
      return $form;
    }

function pathfindertool_first_form_validate($form, &$form_state) {
  if (!($form_state['values']['level'])){
    form_set_error('level', t('You must select an experience level.'));
  }
}

    /**
     * Submit handler for the first form.
     *
     * @see pathfindertool_first_form()
     */
     function pathfindertool_first_form_submit($form, &$form_state) {
      $values = $form_state['values'];

      // Save values to the form storage, add the next step function callback
      $form_state['rebuild'] = TRUE;  // This is very important to have!
      $form_state['storage']['level'] = $values['level'];
      $form_state['storage']['step'] = 'pathfindertool_second_form';
    }

function pathfindertool_second_form($form, &$form_state) {
   if(!empty($form_state['storage']))
    $values = $form_state['storage'];
  $form['role'] = array(
    '#required' => TRUE,
    '#type' => 'radios',
    '#prefix' => '<div class="question-intro-text">Now, select your role in your organization.</div>',
    '#title' => t('What is your role? (pick one)'),
    '#default_value' => isset($values['role']) ? $values['role'] : NULL,
    '#options' => array(
        t('Grant writer'),
        t('Nonprofit Administrator'),
        t('Nonprofit Founder'),
        t('Consultant'))
  );
  $form['back'] = array(
    '#type' => 'submit',
    '#value' => t('Back'),
    '#limit_validation_errors' => array(),
    '#submit' => array('pathfindertool_second_form_submit'),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Next'),
  );
  return $form;
}

/**
 * Submit handler for the second step.
 *
 * @see pathfindertool_second_form_start()
 */
function pathfindertool_second_form_submit($form, &$form_state) {
  $values = $form_state['values'];
  $form_state['rebuild'] = TRUE;
  if (isset($values['back']) && $values['op'] == $values['back']) {
    $input = $form_state['input'];
    // The user clicked the back button, save values that were input
    if (isset($input['role'])) {
      $form_state['storage']['role'] = $input['role'];
    }
    $form_state['storage']['step'] = 'pathfindertool_first_form';
  }
  else {
    $form_state['storage']['role'] = $values['role'];
    $form_state['storage']['step'] = 'pathfindertool_third_form';
  }
}

/**
 * Form that is called third
 */
function pathfindertool_third_form(&$form, &$form_state) {
   if(!empty($form_state['storage']))
    $values = $form_state['storage'];
  $form['topic'] = array(
    '#type' => 'radios',
    '#required' => TRUE,
    '#prefix' => '<div class="question-intro-text">Finally, select the topics that interest you.</div>',
    '#title' => t('Which topics do you want to learn about in the grant lifecycle? (pick one)'),
    '#default_value' => isset($values['topic']) ? $values['topic'] : NULL,
    '#options' => array(
        t('Grant writing'),
        t('Grants Research'),
        t('Grants Management'),
        t('Strategic Planning'))
  );
  $form['back'] = array(
    '#type' => 'submit',
    '#value' => t('Back'),
    '#limit_validation_errors' => array(),
    '#submit' => array('pathfindertool_third_form_submit'),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );

  $form['#submit'][] = 'pathfindertool_third_form_submit';

  return $form;
}

// where I start having problems...
function pathfindertool_third_form_submit($form, &$form_state) {  
    $values = $form_state['values']; 
    $form_state['rebuild'] = TRUE; // need this set to TRUE, see explanation above

  if (isset($values['back']) && $values['op'] == $values['back']) {
  $input = $form_state['input'];
    // The user pushed the back button. 
    if (isset($input['topic'])) {
        $form_state['storage']['topic'] = $input['topic'];
    }
    $form_state['storage']['step'] = 'pathfindertool_second_form';
  }
  else {
    $form_state['storage']['topic'] = $values['topic'];
    $_SESSION['level'] = $form_state['storage']['topic'];
    $_SESSION['role'] = $form_state['storage']['role'];
    $_SESSION['topic'] = $form_state['storage']['topic'];
    $form_state['storage']['step'] = 'redirect page';
  }
}


// not working although I can echo things out here with no problem
function redirect_page() {
    $form_state['rebuild'] = FALSE;
    echo 'something'; // gets called
    $form_state['redirect'] = 'pathfinder/recommendations'; // does NOTHING
}
*/

// function to return results from DB based on what user entered, not finished - works when called
function pathfindertool_recommendations_page() {
    $output = '';
    $result = db_query("SELECT nid, type, title FROM {node} WHERE type = :type", array(':type' => 'resource',));
    if ($result) {
        while ($row = $result->fetchAssoc()) {
            $title = $row['title'];
            $output .= '<div>' . $title . '</div>';
        }
    }
    return $output;
}
1

You can't rebuild AND redirect a form. The redirect property determines where the form should go once it's finished. Setting rebuild means it's not finished. This is why we use &$form_state['storage'] to determine what is to be displayed, save data that needs to persist across steps, etc (note: you should not be using $_SESSION).

ok, I got what you suggested to work on its own. But as for using it with my 3 page form...it still continues to work fine (I rebuilt it line by line) until I get to the FINAL step in the third page submit handler ( can have 1 of the following lines but not both...and I need both): $form_state['storage']['step'] = 'something'; $form_state['redirect'] = 'pathfinder/recommendations'; –

If it's the third and final step, why would you need to rebuild it? In this case, would it not be complete? If you set rebuild to FALSE, it should go to the page you have in redirect.

  • I have to use rebuild in this function otherwise it won't save the value a user selects on the page in case they go back to another page and return (its multipage and they can go back and forth, the script saves their answers). So I HAVE to send them to another function on submission - or rather, when they click 'next'. But when I call a new function, say function gohere() {redirect code } nothing happens (echo works). It only redirects if i put the redirect code in place of the form state i mentioned and get rid of rebuild...in which case it also doesnt save! quandary. y no $_SESSION? – Sage Jan 22 '15 at 19:51
  • Ok, I've added my ENTIRE code to the question now so you can see what I'm working with - this should make it easier to understand. – Sage Jan 22 '15 at 20:51
  • From what I gather from your comment above, when you're on step 3 (the last step), you can either complete the form, or go backwards (in which case it's not complete). So, what you want to probably do is only set rebuild if the button pressed is the one that finishes the form and not the one that goes backwards. Also, your code is a bit hard to read. When you have a chance, checkout these coding standards links: coding standards, coder :) – skh Jan 22 '15 at 22:57
  • Yes! That fixed it. Thank you, thank you, thank you. I just put $form_state['rebuild'] = TRUE (IN the IF statement for the back button). And yes, coding standards...it's on my list of things to do once I get functionality working. But I see your point that it would be helpful to do as I'm getting help also. Thanks again :) – Sage Jan 22 '15 at 23:24
1

Instead of

$form_state['storage']['step'] = 'redirect_page'; // This is for storage only and redirect_page works as string here.

write this :

$form_state['redirect'] = 'pathfinder/recommendations';

Note: $form_state is reference here.

  • Thanks guys. Let me be clear. The $form_state IS being passed by reference in the function calling these arguments (ie. function module_form3_submit(ref)...its a multipage form though and the user needs to press submit to make sure their options are saved to $form_state['storage']['step']; it calls that function and then – Sage Jan 22 '15 at 9:40
  • You should not write the function in that case. Just check the form storage in final submit and use the variable. Or try it using function redirect_page(&$form_state) {. – Sumit Madan Jan 22 '15 at 9:47
  • I don't know what you mean by not write the function. The function is required to process the variables for the third form and to also register if the user clicks back to change their answers. And to register the current option if they do go back and had selected something so that when they return its still there. And I have to have $form_state['rebuild'] = TRUE; as well at the top so no redirection can take place. But when I send the submit button to a new function redirect_page() as you suggest and put the redirect there nothing happens. – Sage Jan 22 '15 at 10:44
1

Try putting it in hook_form_alter.

Per drupal.org's explanation of that item:

Redirects the user to a URL after a form has been processed.

So you need to slide it in before the form is processed, which is what hook_form_alter is for.

You should be able to do something like:

function mymod_form_alter(&$form, &$form_state, $form_id)
  { 
    if($form_id == 'form-to-change'){
      $form_state['redirect'] = "path";
  }
}
  • thanks, I'm confused as to where I would call this pathfinder_form_alter function from? Do I just place it in my code after the processing function...or do I call it from someplace (ie. my themes template.php)? I tried the beginning and end of my code (after the third page submit handler and after the hook_menu call at the top - either way nothing happened)... – Sage Jan 22 '15 at 3:28
  • I'm pretty sure $form_state should be taken by reference. If it's not, any changes made to it will be function-only and cannot affect further processing. – Mołot Jan 22 '15 at 8:55
  • 1
    @Sage, hook_form_alter is a module hook, so in order to use if you'll need to use it with a custom module. If you're unfamiliar with custom modules, you may wish to take a look at the drupal tutorial here: drupal.org/node/1074360 – Jance Jan 22 '15 at 13:47
  • @Mołot, you are correct, that var should be passed as a reference. Will make the edit to reflect the missing & for the reference var. – Jance Jan 22 '15 at 13:48
  • @Jance Thanks Jance. I'm new to all this and clearly didn't explain myself well. This whole thing is about my custom module. I've edited my original question with clarification about the problem and my ENTIRE module code. Not sure where to put a hook form alter in there. – Sage Jan 22 '15 at 20:55
1

With what you have define above, the function pathfinder_recommendations_page would not take in $form and $form_state. You should change it up so that this function is just a regular page callback. You can return a string and then test the path to make sure that the page callback is working.

Then to get your form_state redirect to work, create a submit handler for the form and inside of the submit handler change use the form_state redirect.

[EDIT] I guess it's a bit unclear what exactly you are wanting to achieve here. My assumption was that you wanted the redirect to occur when you submit the form. As I mentioned above, right now your page callback for pathfinder_recommendations_page is not setup to take in the $form and $form_state arguments.

function pathfindertool_menu() {
  //
  $items['pathfinder'] = array(
    'type' => MENU_CALLBACK,
    'title' => 'Pathfinder Tool',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('pathfinder_primary_form'),
    'access callback' => TRUE,
  );
  // define that path
  $items['pathfinder/recommendations'] = array(
    'title' => 'Recommendations',
    'page callback' => 'pathfinder_recommendations_page',
    'access callback' => TRUE,
  );
  return $items;
}
function pathfinder_recommendations_page() {
  // this is the destination page
  return 'anything you like';
}

function pathfinder_primary_form($form, &$form_state) {
  //define form here
}

function pathfinder_primary_form_submit($form, &$form_state) {
  //redirect here
  $form_state['redirect'] = 'pathfinder/recommendations';  
}
  • Thanks for your feedback. I should have mentioned that this is just some of the code from my form...it's part of a 3 page form that collects answers from users with radio buttons on each page and is SUPPOSED to then display results on a new page. I"ve already tried what you suggest and nothing happens. I'm editing above to show you the entire third page submit handler I'm using (note: there is also a wrapper submit handler...maybe I should be placing the redirect there?) – Sage Jan 22 '15 at 3:32
  • ok, I got what you suggested to work on its own. But as for using it with my 3 page form...it still continues to work fine (I rebuilt it line by line) until I get to the FINAL step in the third page submit handler ( can have 1 of the following lines but not both...and I need both): $form_state['storage']['step'] = 'something'; $form_state['redirect'] = 'pathfinder/recommendations'; – Sage Jan 22 '15 at 5:38
  • I'm pretty sure $form_state should be taken by reference. If it's not, any changes made to it will be function-only and cannot affect further processing. – Mołot Jan 22 '15 at 8:55
  • Yes you are right, that was my mistake. – Brian Williams Jan 22 '15 at 14:30
  • @Brian Williams thank you for your help; I edited my question to show you my problem more clearly; I've added my full code with explanation to make it easier. – Sage Jan 22 '15 at 20:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.