Drupalではフォームの扱いがちょっと面倒で自分自身苦手だったので、ちょっとおさらいのためにウィザード形式のマルチフォームの簡単なサンプルを作ってみました。
ウィザード形式ですので、前後のページへの移動ボタンなどもちゃんとあります。この記事の元になったdrupal.orgの記事は以下の通りです。
Tutorial: Ten Step-by-Step Code Samples for Learning Form API
http://drupal.org/node/262422
この記事のSample #10がマルチステップのフォームなのですが、これを拡張して前後のページに移動できるようにして、ページ数をふやしたりコードを整理したものが今回のサンプルモジュールです。自分自身の覚え書きの意味もありあえて記事に残すことにしました。
試してみよう
サンプルはmy_moduleという簡単なモジュールにしましたので試してみるにはこのページの下からダウンロードしてインストールしてください。インストール後はこのモジュールを管理ぺージから有効にすれば準備完了です。テストするにはクリーンURL機能のON/OFFに応じて以下のURLをアクセスします。
クリーンURLがONの場合
http://(DrupalサイトのURL)/my_module/form
クリーンURLがOFFの場合
http://(DrupalサイトのURL)/?q=my_module/form
コードの簡単な説明
以下がこのモジュールのコードです。ちょっと長いので分割して説明していきます。- <?php
- /**
- * @file
- * Example of Multistep Form with Drupal 6.x
- *
- * @author
- * Hideki Ito - PIXTURE STUDIO <http://www.pixture.com>
- */
- /**
- * Implementation of hook_menu()
- */
- function my_module_menu() {
- 'title' => t('Multistep Form Sample'),
- 'page callback' => 'drupal_get_form',
- 'description' => t('My multistep form'),
- 'type' => MENU_CALLBACK,
- );
- return $items;
- }
drupal_get_form('my_module_my_form')がコールされることになります。
- /**
- * Form builder - main
- */
- function my_module_my_form($form_state) {
- if (!$form_state['storage']['page']) {
- // directly invoiked from menu
- $form_state['storage']['page'] = 1;
- }
- $page = $form_state['storage']['page'];
- // get page specific portion of the form
- switch ($page) {
- case 1:
- $form = my_module_my_form_page_1($form_state);
- break;
- case 2:
- $form = my_module_my_form_page_2($form_state);
- break;
- case 3:
- $form = my_module_my_form_page_3($form_state);
- break;
- case 4:
- $form = my_module_my_form_page_4($form_state);
- break;
- }
- // add common buttons to the bottom of the form
- if ($page > 1) {
- '#type' => 'submit',
- '#value' => '<< Back',
- );
- }
- if ($page < 4) {
- '#type' => 'submit',
- '#value' => 'Next >>',
- );
- }
- else { // only on page 4
- '#type' => 'submit',
- '#value' => 'Submit',
- );
- }
- '#type' => 'submit',
- '#value' => 'Reset form',
- );
- return $form;
- }
このページによって各ページ独自のフォーム項目を生成するサブルーチンをコールします。そして、ページ下部にくるボタンは共通化してこの関数の中で定義しています。「Back」ボタンは1ページにはなく、「Next」ボタンは最終ページにはありません。また、最終ページにのみ「Submit」ボタンを置いています。
- /**
- * Form builder for page 1
- */
- function my_module_my_form_page_1($form_state) {
- $default_name = $form_state['values']['name'];
- $default_country = $form_state['values']['country'];
- // load the stored data
- $default_name = $form_state['storage']['page_values_1']['name'];
- $default_country = $form_state['storage']['page_values_1']['country'];
- }
- '#type' => 'markup',
- '#value' => '<h3>Page 1</h3>',
- );
- '#type' => 'textfield',
- '#title' => t('Name'),
- '#default_value' => $default_name,
- '#description' => "Please enter your name.",
- '#size' => 20,
- '#maxlength' => 20,
- );
- '#type' => 'textfield',
- '#title' => t('Country'),
- '#default_value' => $default_country,
- '#description' => "Please enter your country.",
- );
- return $form;
- }
- /**
- * Form builder for page 2
- */
- function my_module_my_form_page_2($form_state) {
- $default_color = $form_state['values']['color'];
- // load the stored data
- $default_color = $form_state['storage']['page_values_2']['color'];
- }
- '#type' => 'markup',
- '#value' => '<h3>Page 2</h3>',
- );
- '#type' => 'radios',
- '#title' => 'Favorite Color',
- 'Blue' => 'Blue',
- 'Red' => 'Red',
- 'Yellow' => 'Yellow',
- ),
- '#default_value' => $default_color,
- '#description' => "Please select your favorite color.",
- );
- return $form;
- }
- /**
- * Form builder for page 3
- */
- function my_module_my_form_page_3($form_state) {
- $default_food = $form_state['values']['food'];
- // load the stored data
- $default_food = $form_state['storage']['page_values_3']['food'];
- }
- '#type' => 'markup',
- '#value' => '<h3>Page 3</h3>',
- );
- '#type' => 'textfield',
- '#title' => 'Favorite Food',
- '#default_value' => $default_food,
- '#description' => "Please enter your favorite food.",
- );
- return $form;
- }
- /**
- * Form builder for page 4
- */
- function my_module_my_form_page_4($form_state) {
- // get stored data
- $name = $form_state['storage']['page_values_1']['name'];
- $country = $form_state['storage']['page_values_1']['country'];
- $color = $form_state['storage']['page_values_2']['color'];
- $food = $form_state['storage']['page_values_3']['food'];
- // generate themed summary table (just make it look nicer)
- );
- );
- );
- );
- );
- $summary = '<h3>Page 4 (Summary)</h3>';
- $summary .= theme('table', $header, $rows);
- $summary .= '<br/>';
- '#type' => 'markup',
- '#value' => $summary,
- );
- return $form;
- }
最終ページは設定項目はなく、確認画面としてそれまでのページで入力した項目をテーブルにして表示しています。
各ページのサブルーチンの先頭で、$form_state['storage']['page_values_X']というのが設定されているかをチェックし、設定されていればその配列から入力項目のデフォルト値を取得してます。これはこの後に出てくるsubmitハンドラでセットするのですが、入力された項目をページごとにまとめて$form_state['storage']['page_values_X'](Xはページ番号)に保管しておきます。
通常、こういったマルチフォームなどの処理はセッション変数を利用して一時的に保管するのですが、この記事の元になった記事でこういった$form_stateを利用した仕組みを使っているのでこのモジュールでもそれにならっています。
- /**
- * Form validator
- */
- function my_module_my_form_validate($form, &$form_state) {
- // we do not validate the page when Back button is pressed
- if ($form_state['clicked_button']['#id'] == 'edit-back') {
- return;
- }
- $page = $form_state['storage']['page'];
- switch ($page) {
- case 1:
- form_set_error('name', 'Please enter your name.');
- }
- form_set_error('country', 'Please enter your country.');
- }
- break;
- case 2:
- form_set_error('color', 'Please enter your favorite color.');
- }
- break;
- case 3:
- form_set_error('food', 'Please enter your favorite food.');
- }
- break;
- case 4:
- // nothing to validate
- break;
- }
- }
あと、この関数の先頭で押されたボタンが「Back」の場合には入力項目の検証はおこなわないようにしています。「Back」の時に検証してしまうと、「Next」で新しいページに来た直後に前のページに戻りたくなったときに、そのページの入力項目をちゃんと入力しないと前のページに戻れないのはよろしくないからです。
- /**
- * Form submit handler
- */
- function my_module_my_form_submit($form, &$form_state) {
- $page = $form_state['storage']['page'];
- if ($form_state['clicked_button']['#id'] == 'edit-back') {
- $form_state['storage']['page']--;
- // save the current page data
- $form_state['storage']['page_values_' . $page] = $form_state['values'];
- }
- else if ($form_state['clicked_button']['#id'] == 'edit-next') {
- $form_state['storage']['page']++;
- // save the current page data
- $form_state['storage']['page_values_' . $page] = $form_state['values'];
- }
- else { // submit button
- //**********************************************************************
- // We have finally done! Add code to do something - save data, etc.
- //**********************************************************************
- drupal_set_message('Your form has been submitted');
- $form_state['redirect'] = 'node'; // Redirects to the top page
- }
- }
「Submit」ボタンが押された場合には、このサンプルでは何もしないでサイトのトップページにジャンプして「Your form has been submitted」とメッセージを表示するようにしています。実際には、ここで入力項目のデータを元に処理をおこなったり、入力データをデータベースに保管したりといった処理をおこなうことになります。「Submit」されて処理がおわった時点で、もう不要となった$form_state['storage']をunsetして削除しています。
- /**
- * Form reset handler
- *
- * Entire form data is cleared and go back to the page 1
- */
- function my_module_my_form_clear($form, &$form_state) {
- // button is clicked
- // new_name part
- $form_state['rebuild'] = TRUE;
- }
- ?>
$form_state['rebuild'] = TRUE;によりフォームAPIは同じページのフォームビルダーを再度コールします。これによりクリアされた内容で同じページのフォームが表示されることになります。このモジュールでは$form_state['storage']をクリアすると、$form_state['storage']['page']も当然なくなりますのでメニューからコールした時の同じ初期状態になり、ページ1のフォームが表示されることになります。
以上です。この記事が誰かの役に立てば幸いです。
ダウンロード
| 添付 | サイズ |
|---|---|
| my_module-6.x-1.x.tar.gz | 2.22 KB |

