Using your hook_form_alter or hook_form_FORM_ID_alter function you can redirect where a node form will go after a user submits the form.

The default behavior for a node form is to go to the new node that the form just created.

If the new node received nid 30, then go to node/30. If, however, you would like to go to a thank you page that has nid of 10, you need to redirect the form to go to node/10 instead.

It's easiest to use hook_form_FORM_ID_alter in a module.

Let's say the module is called my_redirect and you've created a content type that has form_id of my_content_type_node_form. You'll be creating two functions:

my_redirect_form_my_content_type_node_form_alter() - this will add an additional submit handler
my_redirect_node_form_submit() - this will contain the address of the node to redirect to

Within your my_redirect module, you create the following:

function my_redirect_form_my_content_type_node_form_alter(&$form, &$form_state){
    $form['#submit'][] = 'my_redirect_node_form_submit';
}

You're appending to the ['submit']['#submit'] array so that 1) you make sure that the default node_form_submit() function runs and saves the node, and 2) you can override the redirect setting AFTER node_form_submit() sets it.

function my_redirect_node_form_submit($form, &$form_state) {
  // only redirect if node_form_submit() has successfully saved the node
  if ( !empty($form_state['nid']) ) {
     $form_state['redirect'] = 'node/10'; // the address of your thank you page
    // alternatively, you could redirect back to the form using: $form_state['redirect'] = sprintf('node/%d/edit', $form_state['nid']);
   // if you are really, really determined to force the redirect, even if a destination has been set in the url, add this:  unset($_GET['destination']);
   // you could also add a message (this is in addition to whatever message is produced by the nodeapi function): drupal_set_message(t('Congratulations!'));
  }
}

If you were to use the debug function (and woe unto those who dare develop in Drupal without it -- and don't forget to go to /admin/config/development/logging and turn on error messages) and look at your form, you'd see

 [actions] => Array
        (
            [#type] => actions
            [submit] => Array
                (
                    [#type] => submit
                    [#access] => 1
                    [#value] => Save
                    [#weight] => 5
                    [#submit] => Array
                        (
                            [0] => node_form_submit
                            [1] => my_redirect_node_form_submit
                        )

                )

As you can see there are two submit functions being called. And now it's a little more obvious why your custom submit function needed to be assigned to $form['actions']['submit']['#submit'][1].

The next time you use your form, you should be directed to the thank you page at node/10.

Examining the code at node_form_submit is a useful exercise, and helps you to understand why the order in which submit functions occur matters. It is also helpful to look at https://api.drupal.org/api/drupal/includes!form.inc/function/drupal_redi..., which explains how the normal form redirect rules work, and the exceptions.

Note that when you examine that code, you see why you really do NOT want to "unset($form_state['rebuild'])" as you see in many answers on this topic. If 'rebuild' is true after node_form_submit() runs, it is because the node did not save. In this case, you do NOT want to redirect, and you do want to permit Drupal to take the user back to the form, warts and all.

Comments

janekD7’s picture

For Drupal 7.22 you must also add

unset($_GET['destination']);

because $_GET['destination'] overrides $form_state['redirect']
You can also pass array to $form_state['redirect'] which I find very usefull so finally my function looks like:

function my_redirect_node_form_submit($form, &$form_state) {
    unset($_GET['destination']);
    unset($form_state['rebuild']);  // if $form_state['rebuild'] == TRUE, the redirect will be ignored
    $form_state['redirect'] = array('node/10', array('fragment' => 'anchorid')); 
    // array('fragment' => 'anchorid') is used because I needed redirection to an anchor in specific node
}

More info could be found at http://api.drupal.org/api/search/7/drupal_goto because $form_state['redirect'] is passed to this function

highfellow’s picture

Having just spent most of a day staring at this code without managing to get it working, I just wanted to add that in some cases you may need to look at the module weights. If another contrib module is trying to redirect the same form, there will be a conflict which you need to resolve by tweaking the execution order. See: https://drupal.org/node/110238 for details.

Having sorted that out, the above code worked fine for me.

joegl’s picture

This was helpful thanks!

Just want to say that when I was trying to hook into the submit callback for commerce checkout panes my "submit" array looked like this:


$form['submit']['#submit'][]

It must be different based on the what's being edited.

davewilly’s picture

I have noticed a side effect with this, any alterations I am making to the $form_state['values'] in the custom submit handler are not being saved.

jgreep’s picture

I was banging my head until I noticed that I wasn't getting $form_state by reference in the submit handler parameters. It didn't matter what I did, the value was never leaving the handler.

&$form_state

stevecowie’s picture

I had a use case where query parameters have to be passed through in the redirect so the custom function looks like this:

function mymodule_user_login_submit($form, &$form_state) {
  $qitems = drupal_get_query_parameters();
  if(array_key_exists('destination', $qitems)) {
    $destination = $qitems['destination'];
    //check for fragment in destination
    $destination_elements = explode('#', $destination);
    $basedestination = $destination_elements[0];
    $fragment = count($destination_elements) == 2 ? $destination_elements[1] : '';
    unset($qitems['destination']);
    unset($_GET['destination']);
    unset($form_state['rebuild']);
    $form_state['redirect'] = array($basedestination, array('query' => $qitems, 'fragment' => $fragment));
  }
}
DavilaG’s picture

In case somebody is wondering how to do it in Drupal 9, the example below works for me

//Form alter function to add a custom submit function
function MY_MODULE_form_user_form_alter(&$form, &$form_state) {
$form['actions']['submit']['#submit'][] = 'MY_MODULE_CUSTOM_REDIRECT';
}
//Custom submit function to redirect to NODE/1
function MY_MODULE_CUSTOM_REDIRECT(&$form, &$form_state) {
//Redirect to node/1
$form_state->setRedirect('entity.node.canonical', ['node' => 1]);
}