multipage_form_example.module

  1. drupal
    1. 4.7 developer/examples/multipage_form_example.module
    2. 5 developer/examples/multipage_form_example.module

Functions & methods

NameDescription
expand_hidden_array
multipage_form_example_accessImplementation of hook_access().
multipage_form_example_custom_submit
multipage_form_example_custom_validateValidate our form.
multipage_form_example_deleteImplementation of hook_delete().
multipage_form_example_elementsPlaying around here with a new form element to enable storing hidden values in an array fashion. Seems to work fine. Check out $form['test_hidden_array'] above for how to structure the form element
multipage_form_example_formImplementation of hook_form() for multipage_form_example. We don't set ANY #required attributes here - we leave that up to multipage_form_example_pre_render().
multipage_form_example_form_alterImplementation of hook_form_alter(). Here, we set up the 'page' field, which keeps track of what stage the form is in.
multipage_form_example_helpImplementation of hook_help().
multipage_form_example_loadImplementation of hook_load().
multipage_form_example_menuImplementation of hook_menu().
multipage_form_example_node_infoImplementation of hook_node_info().
multipage_form_example_pre_renderThe #pre_render of a form allows us to make changes AFTER validation (unlike hook_form_alter()), but BEFORE the form has actually been displayed. We use it to control which form elements are shown, which are hidden, and which values to set based on…
multipage_form_example_viewImplementation of hook_view().
multipage_form_restore_attributesRestore any form attributes which have been set using multipage_form_set_attribute().
multipage_form_set_attributeSet an attribute on an element array with storing the previous value which may be reverted to using multipage_form_restore_attributes().
multipage_form_set_element_visibilitySet an element's visibility. Elements are gnerally changed to hidden elements. Visibility may be set and reset any number of times.
theme_hidden_array
theme_multipage_form_exampleA custom theme function.
theme_multipage_form_example_node_form

File

developer/examples/multipage_form_example.module
View source
  1. <?php
  2. /**
  3. * Implementation of hook_help().
  4. */
  5. function multipage_form_example_help($section) {
  6. switch ($section) {
  7. case 'admin/modules#description':
  8. return t('An example module showing how to handle multipage forms in FAPI.');
  9. case 'node/add#multipage_form_example':
  10. return t('This will show you an example multipage form submission.');
  11. }
  12. }
  13. /**
  14. * Implementation of hook_menu().
  15. */
  16. function multipage_form_example_menu($may_cache) {
  17. $items = array();
  18. if (!$may_cache) {
  19. $items[] = array('title' => t('multipage form example'),
  20. 'path' => 'node/add/multipage_form_example',
  21. 'access' => TRUE,
  22. );
  23. }
  24. return $items;
  25. }
  26. /**
  27. * Implementation of hook_access().
  28. */
  29. function multipage_form_example_access($op, $node) {
  30. return TRUE;
  31. }
  32. /**
  33. * Implementation of hook_node_info().
  34. */
  35. function multipage_form_example_node_info() {
  36. return array('multipage_form_example' => array('name' => t('multipage form example'), 'base' => 'multipage_form_example'));
  37. }
  38. /**
  39. * Implementation of hook_form() for multipage_form_example. We don't set ANY
  40. * #required attributes here - we leave that up to multipage_form_example_pre_render().
  41. */
  42. function multipage_form_example_form(&$node) {
  43. $form = array();
  44. // 'checkboxes' elements are just ridiculously hard--i recommend just
  45. // using a bunch of individual checkbox elements instead.
  46. // I suspect that multiselects would also be pretty difficult...
  47. // Here's a new form element, which enables you to store hidden values
  48. // in an array. Check out the element definition functions at the bottom
  49. // if you want to use this in a module. Keys show up in the
  50. // $_POST/$form_values like:
  51. // $_POST['edit']['test_hidden_array']['n'] = 'north'
  52. // $form_values['test_hidden_array']['n'] = 'north'
  53. $form['test_hidden_array'] = array(
  54. '#type' => 'hidden_array',
  55. '#values' => array('n' => 'north', 'e' => 'east', 'w' => 'west', 's' => 'south', ),
  56. );
  57. // Helper for our multipage - does field switching, sets options
  58. // based on validated $form_values instead of $_POST, and so forth.
  59. $form['#pre_render'] = array('multipage_form_example_pre_render');
  60. // Title and body, page 1
  61. $form['title'] = array(
  62. '#type' => 'textfield',
  63. '#title' => t('Title'),
  64. '#default_value' => isset($node->title) ? $node->title : NULL,
  65. '#description' => t("Enter a title for your favorites section."),
  66. '#size' => 60,
  67. '#maxlength' => 128,
  68. '#required' => TRUE,
  69. );
  70. $form['body'] = array(
  71. '#type' => 'textarea',
  72. '#title' => t('Description'),
  73. '#default_value' => isset($node->body) ? $node->body : NULL,
  74. '#description' => t('Add any additional info about your favorites.'),
  75. '#rows' => 20,
  76. );
  77. // Person, page 2
  78. $form['person'] = array(
  79. '#type' => 'fieldset',
  80. '#title' => t('Your favorite person'),
  81. );
  82. $form['person']['fav_person'] = array(
  83. '#type' => 'textfield',
  84. '#title' => t('Name'),
  85. '#default_value' => isset($node->fav_person) ? $node->fav_person : NULL,
  86. '#description' => t('Enter their real name, or their code name if they like to fly stealth.'),
  87. '#required' => TRUE,
  88. );
  89. $form['person']['fav_person_desc'] = array(
  90. '#type' => 'textarea',
  91. '#title' => t('Description'),
  92. '#default_value' => isset($node->fav_person_desc) ? $node->fav_person_desc : NULL,
  93. '#description' => t('Juicy details go here...'),
  94. '#required' => TRUE,
  95. );
  96. $form['person']['fav_gummi'] = array(
  97. '#type' => 'checkbox',
  98. '#default_value' => isset($node->fav_gummi) ? $node->fav_gummi : 0,
  99. '#title' => t('Do they like gummi bears?'),
  100. );
  101. // Color and number, page 3
  102. $form['fav_color'] = array(
  103. '#type' => 'select',
  104. '#title' => t('Favorite color'),
  105. '#default_value' => isset($node->fav_color) ? $node->fav_color : 'red',
  106. '#options' => array('red' => t('Red'), 'green' => t('Green'), 'blue' => t('Blue'), 'yellow' => t('Yellow')),
  107. '#required' => TRUE,
  108. );
  109. $form['fav_number'] = array(
  110. '#type' => 'textfield',
  111. '#title' => t('Favorite number'),
  112. '#default_value' => isset($node->fav_number) ? $node->fav_number : NULL,
  113. '#required' => TRUE,
  114. );
  115. // Movie and tv show, page 4
  116. $form['fav_movie'] = array(
  117. '#type' => 'textfield',
  118. '#title' => t('Favorite movie'),
  119. '#default_value' => isset($node->fav_movie) ? $node->fav_movie : NULL,
  120. '#required' => TRUE,
  121. );
  122. $form['fav_tv'] = array(
  123. '#type' => 'radios',
  124. '#title' => t('How often do you watch your favorite tv show?'),
  125. '#default_value' => isset($node->fav_tv) ? $node->fav_tv : 'daily',
  126. '#options' => array('daily' => t('Daily'), 'weekly' => t('Weekly'), 'monthly' => t('Monthly'),),
  127. '#required' => TRUE,
  128. );
  129. // Add a back button
  130. $form['back'] = array(
  131. '#type' => 'button',
  132. '#value' => t('Back'),
  133. '#weight' => 35,
  134. );
  135. return $form;
  136. }
  137. function theme_multipage_form_example_node_form($form) {
  138. $content = '';
  139. if (in_array($form['page']['#value'], array(3,4))) {
  140. $content .= '<p>'. t('Your favorite person is %person, and they %like gummi bears.', array('%person' => check_plain($form['person']['fav_person']['#value']), '%like' => ($form['person']['fav_gummi']['#value'] ? t('like') : t('don\'t like')))) .'</p>';
  141. }
  142. if (in_array($form['page']['#value'], array(4))) {
  143. $content .= '<p>'. t('Your favorite color is %color, and your favorite number is %number.', array('%color' => check_plain($form['fav_color']['#value']), '%number' => check_plain($form['fav_number']['#value'])));
  144. }
  145. foreach (element_children($form) as $key) {
  146. $content .= form_render($form[$key]);
  147. }
  148. return $content;
  149. }
  150. /**
  151. * Implementation of hook_form_alter(). Here, we set up the 'page' field,
  152. * which keeps track of what stage the form is in.
  153. */
  154. function multipage_form_example_form_alter($form_id, &$form) {
  155. // Make sure it's our multipage form.
  156. if ($form_id == 'multipage_form_example_node_form') {
  157. // Determine which page of the multipage form we're on. We don't do
  158. // any incrementing here - that's something that our #pre_render'er
  159. // will do when this page of the form has successfully validated.
  160. $form['page'] = array(
  161. '#type' => 'hidden',
  162. '#value' => isset($_POST['edit']['page']) ? $_POST['edit']['page'] : 1,
  163. );
  164. // If back button is pressed, back up the form stage, Also, if preview
  165. // is hit we need to decrement the counter here to keep us on the same page.
  166. if ($_POST['op'] == t('Back')) {
  167. $form['page']['#value']--;
  168. }
  169. // This modifies the form for validation purposes. once validation is
  170. // completed, it'll be called one more time (through Drupal's Form API)
  171. // at which point it'll advance the form to the next page.
  172. multipage_form_example_pre_render($form_id, $form, FALSE);
  173. // Here we're augmenting the regular node form validation/submission with
  174. // some of our own. Note that these are inside the conditional check for
  175. // this particular form.
  176. $form['#validate'] = array_merge($form['#validate'], array('multipage_form_example_custom_validate' => array()));
  177. $form['#submit'] = array_merge($form['#submit'], array('multipage_form_example_custom_submit' => array()));
  178. }
  179. return $form;
  180. }
  181. /**
  182. * Validate our form.
  183. */
  184. function multipage_form_example_custom_validate() {
  185. global $form_values;
  186. // validate our number, but don't bother if the back button was hit
  187. if (($form_values['page'] == 3 ) && !is_numeric($form_values['fav_number']) && ($_POST['op'] != t('Back'))) {
  188. form_set_error('fav_number', t('Favorite number is a <em>number</em>, dummy!'));
  189. }
  190. }
  191. /**
  192. * The #pre_render of a form allows us to make changes AFTER validation (unlike
  193. * hook_form_alter()), but BEFORE the form has actually been displayed. We use
  194. * it to control which form elements are shown, which are hidden, and which values
  195. * to set based on validate elements, not $_POST. This is a necessity for our
  196. * complicated multipage example form.
  197. */
  198. function multipage_form_example_pre_render($form_id, &$form, $next_page = TRUE) {
  199. global $form_values;
  200. // Make sure it's our multipage form.
  201. if ($form_id == 'multipage_form_example_node_form') {
  202. // Are we ready for the next page in our form?
  203. if ($next_page && isset($_POST['edit']['page']) && ($_POST['op'] != t('Back')) && ($_POST['op'] != t('Preview'))) {
  204. $form['page']['#value'] = $form['page']['#value'] + 1;
  205. }
  206. // Validation errors? Show previous page for correcting.
  207. if (form_get_errors()) {
  208. $form['page']['#value']--;
  209. }
  210. // Modify the #required/#type values depending on our current page.
  211. // The arrays tell us the pages the changes should take place in.
  212. // Title/body, stage 1
  213. multipage_form_set_element_visibility($form['title'], in_array($form['page']['#value'], array(1)));
  214. multipage_form_set_element_visibility($form['body'], in_array($form['page']['#value'], array(1)));
  215. // Person, stage 2
  216. multipage_form_set_element_visibility($form['person'], in_array($form['page']['#value'], array(2)));
  217. multipage_form_set_element_visibility($form['person']['fav_person'], in_array($form['page']['#value'], array(2)));
  218. multipage_form_set_element_visibility($form['person']['fav_person_desc'], in_array($form['page']['#value'], array(2)));
  219. multipage_form_set_element_visibility($form['person']['fav_gummi'], in_array($form['page']['#value'], array(2)), $next_page);
  220. // Color and number, page 3
  221. multipage_form_set_element_visibility($form['fav_color'], in_array($form['page']['#value'], array(3)));
  222. multipage_form_set_element_visibility($form['fav_number'], in_array($form['page']['#value'], array(3)));
  223. // Movie and tv show, page 4
  224. multipage_form_set_element_visibility($form['fav_movie'], in_array($form['page']['#value'], array(4)));
  225. multipage_form_set_element_visibility($form['fav_tv'], in_array($form['page']['#value'], array(4)), $next_page);
  226. // Buttons
  227. multipage_form_set_element_visibility($form['back'], in_array($form['page']['#value'], array(2, 3, 4)), $next_page);
  228. multipage_form_set_element_visibility($form['preview'], in_array($form['page']['#value'], array(4)), $next_page);
  229. multipage_form_set_element_visibility($form['submit'], in_array($form['page']['#value'], array(4)), $next_page);
  230. // The button text actually helps determine if a form has actually been
  231. // submitted because the name is also the value of a clicked button. By
  232. // changing it for building, but not rendering, the form is not fully
  233. // sumbitted until we name it 'Submit', the usual value.
  234. if ($next_page) {
  235. $submit_text = array(NULL, t('Next (person)'), t('Next (color/number)'), t('Next (tv/movie)'), t('Submit'));
  236. $form['submit']['#value'] = $submit_text[$form['page']['#value']];
  237. }
  238. }
  239. }
  240. // Handles form submission. We're just throwing our data into the variable
  241. // table to keep things simple
  242. function multipage_form_example_custom_submit() {
  243. global $form_values;
  244. foreach ($form_values as $key => $value) {
  245. if (in_array($key, array('fav_person', 'fav_person_desc', 'fav_gummi', 'fav_color', 'fav_number', 'fav_movie', 'fav_tv',))) {
  246. $array[$key] = $value;
  247. }
  248. }
  249. // A little hack so we can save new node info properly to the variable table
  250. if (isset($form_values['nid'])) {
  251. $nid = $form_values['nid'];
  252. }
  253. else {
  254. $nid = db_result(db_query("SELECT id FROM {sequences} WHERE name = 'node_nid'"));
  255. }
  256. variable_set('multipage_form_example_'. $nid, $array);
  257. }
  258. /**
  259. * Implementation of hook_load().
  260. *
  261. * Now that we've defined how to manage the node data in the database, We
  262. * need to tell Drupal how to get the node back out. This hook is called
  263. * every time a node is loaded, and allows us to do some loading of our own.
  264. */
  265. function multipage_form_example_load($node) {
  266. $additions = variable_get('multipage_form_example_'. $node->nid, NULL);
  267. $additions = (object) $additions;
  268. return $additions;
  269. }
  270. /**
  271. * Implementation of hook_delete().
  272. *
  273. * Clean up our data in variable table.
  274. */
  275. function multipage_form_example_delete($node) {
  276. // Notice that we're matching all revision, by using the node's nid.
  277. variable_del('multipage_form_example_'. $node->nid);
  278. }
  279. /**
  280. * Implementation of hook_view().
  281. *
  282. * This is a typical implementation that simply runs the node text through
  283. * the output filters.
  284. */
  285. function multipage_form_example_view(&$node, $teaser = FALSE, $page = FALSE) {
  286. $node = node_prepare($node, $teaser);
  287. $favorites = theme('multipage_form_example', $node);
  288. $node->body .= $favorites;
  289. $node->teaser .= $favorites;
  290. }
  291. /**
  292. * A custom theme function.
  293. *
  294. * By using this function to format our node-specific information, themes
  295. * can override this presentation if they wish. We also wrap the default
  296. * presentation in a CSS class that is prefixed by the module name. This
  297. * way, style sheets can modify the output without requiring theme code.
  298. */
  299. function theme_multipage_form_example($node) {
  300. $person_display = t('Your favorite person is %person, and they %like gummi bears. <br \>', array('%person' => check_plain($node->fav_person), '%like' => ($node->fav_gummi ? t('like') : t('don\'t like'))));
  301. $color_number_display = t('Your favorite color is %color, and your favorite number is %number. <br \>', array('%color' => check_plain($node->fav_color), '%number' => check_plain($node->fav_number)));
  302. $tv_movie_display = t('Your favorite movie is %movie, and you watch your favorite tv show %watch.', array('%movie' => check_plain($node->fav_movie), '%watch' => check_plain($node->fav_tv)));
  303. $output = '<div class="multipage_form_example">';
  304. $output .= $person_display . $color_number_display . $tv_movie_display;
  305. $output .= '</div>';
  306. return $output;
  307. }
  308. /**
  309. * Set an element's visibility. Elements are gnerally changed to hidden
  310. * elements. Visibility may be set and reset any number of times.
  311. *
  312. * @param $element
  313. * The form element array to modify.
  314. * @param $visible
  315. * The desired visibity of the form element.
  316. * @param $next_page
  317. * Boolean value, TRUE if the next page is about to be rendered, FALSE otherwise.
  318. */
  319. function multipage_form_set_element_visibility(&$element, $visible, $next_page = TRUE) {
  320. multipage_form_restore_attributes($element);
  321. if (!$visible) {
  322. switch ($element['#type']) {
  323. case 'textfield':
  324. case 'textarea':
  325. case 'select':
  326. multipage_form_set_attribute($element, '#type', 'hidden');
  327. multipage_form_set_attribute($element, '#required', FALSE);
  328. break;
  329. case 'radios':
  330. // Radios elements cannot be hidden unless they have been processed.
  331. if ($next_page) {
  332. multipage_form_set_attribute($element, '#type', 'hidden');
  333. multipage_form_set_attribute($element, '#required', FALSE);
  334. }
  335. break;
  336. case 'radio':
  337. case 'checkbox':
  338. // We can't change these to hidden until right before the next page is rendered, otherwise
  339. // the value is lost sometimes.
  340. if ($next_page) {
  341. multipage_form_set_attribute($element, '#type', 'hidden');
  342. }
  343. break;
  344. case 'fieldset':
  345. multipage_form_set_attribute($element, '#type', NULL);
  346. break;
  347. case 'button':
  348. if ($next_page) {
  349. multipage_form_set_attribute($element, '#type', 'value');
  350. }
  351. break;
  352. case 'submit':
  353. if ($next_page) {
  354. multipage_form_set_attribute($element, '#type', 'button');
  355. }
  356. break;
  357. }
  358. }
  359. foreach (element_children($element) as $key) {
  360. multipage_form_set_element_visibility($element[$key], $visible, $next_page);
  361. }
  362. }
  363. /**
  364. * Set an attribute on an element array with storing the previous value which
  365. * may be reverted to using multipage_form_restore_attributes().
  366. *
  367. * @param $element
  368. * The form element array to modify.
  369. * @param $key
  370. * A key of the form element array.
  371. * @param $new_value
  372. * The new value for the attribute.
  373. */
  374. function multipage_form_set_attribute(&$element, $key, $new_value) {
  375. $element['#multipage_form_original_'. $key] = $element[$key];
  376. $element[$key] = $new_value;
  377. }
  378. /**
  379. * Restore any form attributes which have been set using
  380. * multipage_form_set_attribute().
  381. *
  382. * @param $element
  383. * The form element array to restore.
  384. */
  385. function multipage_form_restore_attributes(&$element) {
  386. foreach (array_filter(array_keys($element), create_function('$key', 'return (strpos($key, "#multipage_form_original_") === 0);')) as $key) {
  387. $element[str_replace('#multipage_form_original_', '', $key)] = $element[$key];
  388. }
  389. }
  390. /**
  391. * Playing around here with a new form element to enable storing hidden values in an array fashion.
  392. * Seems to work fine. Check out $form['test_hidden_array'] above for how to structure the form
  393. * element
  394. */
  395. function multipage_form_example_elements() {
  396. $type['hidden_array'] = array('#input' => TRUE, '#process' => array('expand_hidden_array' => array()), '#tree' => TRUE);
  397. return $type;
  398. }
  399. function expand_hidden_array($element) {
  400. $values = is_array($element['#values']) ? $element['#values'] : array();
  401. $element['#tree'] = TRUE;
  402. foreach ($values as $key => $value) {
  403. $element[$key] = array('#type' => 'hidden', '#processed' => TRUE, '#value' => $value);
  404. }
  405. return $element;
  406. }
  407. function theme_hidden_array($element) {
  408. return $element['#children'];
  409. }
Login or register to post comments