Growing Venture Solutions - GVS - forms http://growingventuresolutions.com/taxonomy/term/110/0 en Drupal 7 multistep forms using variable functions http://growingventuresolutions.com/blog/drupal-7-multistep-forms-using-variable-functions <p>I like building forms. So much so that I've even been teased about it. Despite that I want to share how multistep forms have changed for Drupal 7 and to expand on how you can use variable functions to achieve cleaner and easier form step logic, including easily moving backwards in forms. Understanding multistep in Drupal 7 was prompted by my need to create easy forms for an internal GVS project that will hopefully launch soon.</p> <h3>Multistep in Drupal 7</h3> <p>In Drupal 6 to carry data back to your form builder you set the <code>storage</code> key of <code>$form_state</code> in your submit handler. <strike>In Drupal 7</strike>, upon return to your builder after submission, you carry data over by keeping the Form API from pulling the form array out of cache*. You do so by setting <code>$form_state[&#039;rebuild&#039;]</code> to <code>TRUE</code> in your validate or submit handlers. Another change is the first argument of your builder must be <code>$form</code> because of <a href="http://drupal.org/update/modules/6/7#hook_forms_signature">changes to drupal_get_form()</a>. <code>&amp;$form_state</code> is now your second argument to your form builder.</p> <p>Update: 'rebuild' existed in Drupal 6 (thanks Wim) but now seems to be required for multistep to work in Drupal 7.</p> <p>Drupal 7:<br /> <div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">// Form builder definition.<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">my_form</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {...}<br /><br /></span><span style="color: #FF8000">// Form submit handler.<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">my_form_builder_submit</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #FF8000">// Trigger multistep.<br />&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'rebuild'</span><span style="color: #007700">] = </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">;<br />&nbsp; </span><span style="color: #FF8000">// Store values that will be available when we return to the definition.<br />&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'values'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$values</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p> <p>Let's look at a example:<br /> <div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">// multistep_simple, our form builder function.<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">multistep_simple</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #FF8000">// Check if storage contains a value. A value is set only after the form is submitted and 'rebuild' is set to TRUE.<br />&nbsp; </span><span style="color: #007700">if (!empty(</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'myvalue'</span><span style="color: #007700">])) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Display a message with the submitted value.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">drupal_set_message</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">"You submitted: @name"</span><span style="color: #007700">, array(</span><span style="color: #DD0000">'@name' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'myvalue'</span><span style="color: #007700">])));<br />&nbsp; }<br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'name'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'textfield'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Name'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#description' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Enter your name'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#required' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">,<br />&nbsp; );<br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'submit'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'submit'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Submit'</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; return </span><span style="color: #0000BB">$form</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #FF8000">// Our submit handler for multistep_simple.<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">multistep_simple_submit</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #FF8000">// Tell FAPI to rebuild.<br />&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'rebuild'</span><span style="color: #007700">] = </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">;<br />&nbsp; </span><span style="color: #FF8000">// Store submitted value.<br />&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'myvalue'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">][</span><span style="color: #DD0000">'name'</span><span style="color: #007700">];<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p> <p>We could of course set the message in the submit handler but I hope the example helps convey the two points of the definition arguments and setting rebuild in the submit handler.</p> <h3>Multistep with variable functions and FAPI handler convention</h3> <p>Using variable functions is a great way to make readable form step logic. And, if you combine this method with Drupal's form handler definition style and you can easily make advanced forms.</p> <p>Update: This is by no means a method that exists only in Drupal 7. This is just <a href="http://pingvision.com/blog/ben-jeavons/2009/multi-step-forms-drupal-6-using-variable-functions">a process I've written about before</a> for making form-step logic easier to code and extend. What I'm writing about here is an expansion on that process.</p> <p>The components:</p> <ol> <li>Store step names (function definitions) in <code>$form_state[&#039;storage&#039;]</code></li> <li>Return the current step's form array in the main FAPI builder</li> <li>Invoke validate and submit handlers for a individual step by appending '_validate' or '_submit' to the step definition</li> </ol> <p>Let me start with the form builder and submit handler for illustration. The form ID known to FAPI will be <code>multistep_form</code> and <code>multistep_form_submit</code> the submit handler. The builder uses functions defined in storage to know which step to return and the submit handler uses FAPI convention to call a individual step's validate or submit.</p> <p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">// Primary form builder.<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">multistep_form</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #FF8000">// Initialize.<br />&nbsp; </span><span style="color: #007700">if (</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'rebuild'</span><span style="color: #007700">]) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Don't hang on to submitted data in form state input.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'input'</span><span style="color: #007700">] = array();<br />&nbsp; }<br />&nbsp; if (empty(</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">])) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// No step has been set so start with the first.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'step' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'multistep_form_start'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; );<br />&nbsp; }<br /><br />&nbsp; </span><span style="color: #FF8000">// Return the form for the current step.<br />&nbsp; </span><span style="color: #0000BB">$function </span><span style="color: #007700">= </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'step'</span><span style="color: #007700">];<br />&nbsp; </span><span style="color: #0000BB">$form </span><span style="color: #007700">= </span><span style="color: #0000BB">$function</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">);<br />&nbsp; return </span><span style="color: #0000BB">$form</span><span style="color: #007700">;<br />}<br /><br /></span><span style="color: #FF8000">// Primary submit handler.<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">multistep_form_submit</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #0000BB">$values </span><span style="color: #007700">= </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">];<br />&nbsp; </span><span style="color: #FF8000">// Check if we're moving back or forward in the form.<br />&nbsp; </span><span style="color: #007700">if (isset(</span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'back'</span><span style="color: #007700">]) &amp;&amp; </span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'op'</span><span style="color: #007700">] == </span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'back'</span><span style="color: #007700">]) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Code for moving in reverse left out for now...<br />&nbsp; </span><span style="color: #007700">}<br />&nbsp; else {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Record the current step.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$step </span><span style="color: #007700">= </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'step'</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'steps'</span><span style="color: #007700">][] = </span><span style="color: #0000BB">$step</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Call step submit handler if it exists.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if (</span><span style="color: #0000BB">function_exists</span><span style="color: #007700">(</span><span style="color: #0000BB">$step </span><span style="color: #007700">. </span><span style="color: #DD0000">'_submit'</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$function </span><span style="color: #007700">= </span><span style="color: #0000BB">$step </span><span style="color: #007700">. </span><span style="color: #DD0000">'_submit'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Current step's submit handler will set the next step.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$function</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }<br />&nbsp; return;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p> <p>Step submit handlers specify the next step to use and step builders can skip steps.</p> <p>Here's a diagram depicting the flow of standard FAPI and how the builder and handlers call off to a individual step.</p> <p><img src="http://growingventuresolutions.com/gvsfiles/multistep-form-diagram.png" /></p> <p>Here's an example individual step and it's submit handler:</p> <p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">multistep_form_start</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'musician'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'radios'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Choose a musician'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#options' </span><span style="color: #007700">=&gt; array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'davis' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Miles Davis'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'coltrane' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'John Coltrane'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'corea' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Chick Corea'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'brubeck' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Dave Brubeck'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'other' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Other'</span><span style="color: #007700">)<br />&nbsp;&nbsp;&nbsp; ),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; isset(</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'musician'</span><span style="color: #007700">]) ? </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'musician'</span><span style="color: #007700">] : </span><span style="color: #0000BB">NULL</span><span style="color: #007700">,<br />&nbsp; );<br />&nbsp; </span><span style="color: #FF8000">// Next button.<br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'submit'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'submit'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Next'</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; return </span><span style="color: #0000BB">$form</span><span style="color: #007700">;<br />}<br /><br />function </span><span style="color: #0000BB">multistep_form_start_submit</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #FF8000">// Trigger multistep, there are more steps.<br />&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'rebuild'</span><span style="color: #007700">] = </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">;<br />&nbsp; </span><span style="color: #0000BB">$values </span><span style="color: #007700">= </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">];<br />&nbsp; if (isset(</span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'back'</span><span style="color: #007700">]) &amp;&amp; </span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'op'</span><span style="color: #007700">] == </span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'back'</span><span style="color: #007700">]) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// User is moving back from this form, clear our storage.<br />&nbsp;&nbsp;&nbsp; // [...]<br />&nbsp; </span><span style="color: #007700">}<br />&nbsp; else if (</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">][</span><span style="color: #DD0000">'musician'</span><span style="color: #007700">] == </span><span style="color: #DD0000">'other'</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// User chose 'other' so inject an intermediary step.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'musician'</span><span style="color: #007700">] = </span><span style="color: #0000BB">NULL</span><span style="color: #007700">; </span><span style="color: #FF8000">// Clear out because of our define musician step uses key 'musician' as well.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'step'</span><span style="color: #007700">] = </span><span style="color: #DD0000">'multistep_form_define_musician'</span><span style="color: #007700">;<br />&nbsp; }<br />&nbsp; else {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// We could do something with the values here, like saving etc...<br />&nbsp;&nbsp;&nbsp; // Hold on to value.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'musician'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">][</span><span style="color: #DD0000">'musician'</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Set the next step.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'step'</span><span style="color: #007700">] = </span><span style="color: #DD0000">'multistep_form_songs'</span><span style="color: #007700">;<br />&nbsp; }<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p> <p>The above example defines a single step and sets us up to move through the chain of several. Let's look at the next step and define how we could move backwards. We'll redefine the primary submit handler now.</p> <p>Update: The form property <code>#limit_validation_errors</code> and a element submit property are required on back buttons should any step have anything that would trigger validation.</p> <p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">// Primary submit handler.<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">multistep_form_submit</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #0000BB">$values </span><span style="color: #007700">= </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">];<br />&nbsp; if (isset(</span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'back'</span><span style="color: #007700">]) &amp;&amp; </span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'op'</span><span style="color: #007700">] == </span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'back'</span><span style="color: #007700">]) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Moving back in form.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$step </span><span style="color: #007700">= </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'step'</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Call current step submit handler if it exists to unset step form data.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if (</span><span style="color: #0000BB">function_exists</span><span style="color: #007700">(</span><span style="color: #0000BB">$step </span><span style="color: #007700">. </span><span style="color: #DD0000">'_submit'</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$function </span><span style="color: #007700">= </span><span style="color: #0000BB">$step </span><span style="color: #007700">. </span><span style="color: #DD0000">'_submit'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$function</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Remove the last saved step so we use it next.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$last_step </span><span style="color: #007700">= </span><span style="color: #0000BB">array_pop</span><span style="color: #007700">(</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'steps'</span><span style="color: #007700">]);<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'step'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$last_step</span><span style="color: #007700">;<br />&nbsp; }<br />&nbsp; else {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Record step.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$step </span><span style="color: #007700">= </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'step'</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'steps'</span><span style="color: #007700">][] = </span><span style="color: #0000BB">$step</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Call step submit handler if it exists.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if (</span><span style="color: #0000BB">function_exists</span><span style="color: #007700">(</span><span style="color: #0000BB">$step </span><span style="color: #007700">. </span><span style="color: #DD0000">'_submit'</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$function </span><span style="color: #007700">= </span><span style="color: #0000BB">$step </span><span style="color: #007700">. </span><span style="color: #DD0000">'_submit'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$function</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }<br />&nbsp; return;<br />}<br /><br />function </span><span style="color: #0000BB">multistep_form_songs</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'song'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'textfield'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Favorite recording?'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; isset(</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'song'</span><span style="color: #007700">]) ? </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'song'</span><span style="color: #007700">] : </span><span style="color: #0000BB">NULL</span><span style="color: #007700">,<br />&nbsp; );<br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'unknown'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'checkbox'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">"I don't know"</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'back'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'submit'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Back'</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'submit'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'submit'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Next'</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; <br />&nbsp; return </span><span style="color: #0000BB">$form</span><span style="color: #007700">;<br />}<br /><br />function </span><span style="color: #0000BB">multistep_form_songs_submit</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #0000BB">$values </span><span style="color: #007700">= </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">];<br />&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'rebuild'</span><span style="color: #007700">] = </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">;<br />&nbsp; if (isset(</span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'back'</span><span style="color: #007700">]) &amp;&amp; </span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'op'</span><span style="color: #007700">] == </span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'back'</span><span style="color: #007700">]) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// User is moving back from this form, clear our storage.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'song'</span><span style="color: #007700">] = </span><span style="color: #0000BB">NULL</span><span style="color: #007700">;<br />&nbsp; }<br />&nbsp; else if (</span><span style="color: #0000BB">$values</span><span style="color: #007700">[</span><span style="color: #DD0000">'unknown'</span><span style="color: #007700">]) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Skip to confirm step.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'step'</span><span style="color: #007700">] = </span><span style="color: #DD0000">'multistep_form_confirm'</span><span style="color: #007700">;<br />&nbsp; }<br />&nbsp; else {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'song'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">][</span><span style="color: #DD0000">'song'</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Set the next step.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'storage'</span><span style="color: #007700">][</span><span style="color: #DD0000">'step'</span><span style="color: #007700">] = </span><span style="color: #DD0000">'multistep_form_heard'</span><span style="color: #007700">;<br />&nbsp; }<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p> <p>In our second step form builder (<code>multistep_form_songs</code>) we provide a back button. The primary submit handler allows the current step to remove stored data (to avoid a previously set default value when we return) and then removes the most recent step so when returned to the primary builder the previous step is used.</p> <p>A full multistep example is provided in the attachment. You'll need to rename the files excluding the '.txt' extension to use. Some assumptions are made, of course, so adjustment for your use cases is required.</p> <p>P.S.<br /> If you didn't catch it, in Drupal 7 you are not limited to <code>&#039;storage&#039;</code> for carrying data across requests. It's used in most of the examples, but you'll see that it's not required.<br /> *I'm not clear on some core implementation specifics regarding the form cache so if you know please share!</p> <table id="attachments" class="sticky-enabled"> <thead><tr><th>Attachment</th><th>Size</th> </tr></thead> <tbody> <tr class="odd"><td><a href="http://growingventuresolutions.com/gvsfiles/multistep.module.txt">multistep.module.txt</a></td><td>9.85 KB</td> </tr> <tr class="even"><td><a href="http://growingventuresolutions.com/gvsfiles/multistep.info_.txt">multistep.info_.txt</a></td><td>94 bytes</td> </tr> </tbody> </table> http://growingventuresolutions.com/blog/drupal-7-multistep-forms-using-variable-functions#comments Planet Drupal Drupal 7 forms Tue, 06 Apr 2010 17:41:46 +0000 Ben 841 at http://growingventuresolutions.com