Multi-step Node Forms in Drupal 6
on
Multi-step Node Forms in Drupal 6
$form_state['rebuild'] to TRUE. After trying a few different ways and a bit of searching, I found the solution. The trick is to hide the 'submit' button and use hook_form_alter() on the 'preview' button to regenerate the form for step 2. However, this is probably best explained with some sample code to illustrate.
The first thing you need to do is to define the node form. We're going to use a simple two-step form. On the first page will be the node title and body area, and on the second page a textarea for additional information. Which page of the multistep form to display is determined by $form_state['storage']['step']. As we will see shortly $form_state['storage']['step'] gets set when the first page of the form is submitted.
/**
* Implement hook_form().
*/
function multistep_form(&$node, $form_state) {
// Initial step: display title and body fields.
if (!isset($form_state['storage']['step'])) {
$form['title'] = array(
'#title' => t('Title'),
'#type' => 'textfield',
'#required' => TRUE,
'#default_value' => isset($form_state['storage']['title']) ? $form_state['storage']['title'] : $node->title,
);
$form['body_field'] = node_body_field($node, t('Body'), 1);
}
// Second step: display
else {
$form['additional_info'] = array(
'#title' => t('Additional information'),
'#type' => 'textarea',
'#required' => TRUE,
'#default_value' => isset($form_state['storage']['additional_info']) ? $form_state['storage']['additional_info'] : $node->additional_info,
);
}
return $form;
}hook_form_alter() we can change the first page of the form and make it into a multistep form. We can identify the first step of the form by $form_state['storage'][step'] and so only call multistep_make_node_multistep()for that step. This prevents us from hiding the submit button on the final page.
/**
* Implement hook_form_alter().
*/
function multistep_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'multistep_node_form') {
$node = $form['#node'];
// Page 1, $form_state['storage']['step'] isn't set yet, so display first form.
if (empty($form_state['storage'][step'])) {
// Hide everything except title, body and button fields.
$fields = array('title', 'body');
multistep_make_node_multistep($form, $fields, 'multistep_step1_form_next_handler');
}
}
}hook_form_alter() function rather than the form id specific one.
The multistep_make_node_multistep() function takes in an array of fields that should not be hidden, along with the name of the submit function to use with the 'preview' button. Any field that doesn't appear in the array, other than hidden fields and a few other special ones, are prevented from being displayed by setting #access to FALSE.
function multistep_make_node_multistep(&$form, $fields, $submit_handler) {
// Hide all the elements we don't want.
foreach (element_children($form) as $child) {
if ($child != 'buttons' && !in_array($child, $fields) &&
(empty($form[$child]['#type']) ||
($form[$child]['#type'] != 'hidden'
&& $form[$child]['#type'] != 'value'
&& $form[$child]['#type'] != 'token'))) {
$form[$child]['#access'] = FALSE;
}
}
// Hide the submit button.
$form['buttons']['submit']['#access'] = FALSE;
// Change the 'preview' button to 'Next' and set the submit handler.
$form['buttons']['preview'] = array(
'#type' => 'submit',
'#value' => t('Next'),
'#weight' => 50,
'#submit' => array($submit_handler),
);
}$form_state['storage']['step'] to 1.
function multistep_step1_form_next_handler($form, &$form_state) {
$form_state['storage']['step'] = 1;
}hook_form_alter() wasn't hiding all form fields. dww also provides a sample module which implements a simple two step node form which you can download and try out. This post has been very useful to me. Thanks for sharing this, Stella!
In my case I also needed a custom validation handler.
However, I was stuck with the $form_state['storage']['step']
When I implemented my own validation handler like this
<?php mymodule_validation_handler($form, &$form_state) { dsm($form_state} ?>
I get that the storage array is NULL! And the thing is the function does get called!
It turned out that the storage array simply doesn't POST when you define values in the hook_form().
I also learned that the flow is like: validation handler -> submit handler -> hook_form
Lastly, I also need to insert the node into a database. Could you please help me with this? How do I access the $node object? Where and when (last step) do I call hook_insert ?
I'll remembering one thing: Multi-step nodes are apparently a hard thing to do in drupal!
I managed to access the node object from within hook_insert.
There's one bottleneck though. It really need to access $form_state['storage'] but I have no idea how I can access it from within hook_insert. How would you implement that? How do I pass the $form_state variable to hook_insert? Is there a workaround for this?