フォーム定義を動的に変更する
Ethna でのアクションフォームの定義は、以下のように固定の配列として定義します。つまり、クラスのメンバとして宣言するため、PHP では関数コールや変数の形でフォーム定義を動的に定義することはできません。これは PHP の言語仕様上の制限です*1
ここでは、そうした制限を越えて、フォーム定義をsubmit後に変更する方法を示します。
$form = array( 'sample' => array( // 正しいフォーム定義 'type' => VAR_TYPE_INT, 'name' => 'sample', // ... ), 'invalid' => array( // 間違ったフォーム定義 'type' => $dynamic_type, // 変数は駄目 'name' => _('invalid'), // gettext 等、動的な呼び出しは× // ... ),
書いた人 | key | ---------- | 新規作成 |
書いた人 | mumumu | 2009-01-28 | 最新版に追随する形で全面的に修正 |
書いた人 | mumumu | 2009-06-03 | フォームヘルパが絡んだ場合の記述を追加 |
書いた人 | DQNEO | 2010-11-11 | ActionFormのform定義を直接変更する方法を追加 |
フォーム定義を動的にしなければならない理由
動的にフォーム定義を Ajax 等で動的にブラウザ側のインターフェイスを変更するようになった今にあっては、submitされた値、または データベースやセッションの値に応じて、フォーム定義を変更したいとの要求は自然に出てくるでしょう。
ひとつの例として、SELECT ボックスを複数段展開させなければいけない場合をあげてみます。都道府県を選んだ後に、市町村のSELECTボックスの値を変えなければならなくなったら、固定のフォーム定義ではうまくいかないでしょう
都道府県: <select name="prefecture"> <option value="1">北海道</option> ... </select> 市町村: <select name="city"> <option value="1">北海道なら札幌</option> <option value="2">神奈川なら横浜</option> ... </select>
Ethna 2.5.0 以降でのやり方
Ethna 2.5.0 以降では、フォーム定義を動的に変更するための場所を統一するためのAPIが整備されました。フォーム定義はアクションフォームに関することなので、Ethna_ActionForm クラスのメソッドで統一するのがスマートです。
よって、以下のようなAPI が定義されています。
フォーム定義変更専用のヘルパメソッド
Ethna 2.5.0 以降では、Ethna_ActionForm#setFormDef_PreHelper を使います。このメソッドを呼び出す前に、Ethna_Session, Ethna_Backend などがすべて初期化されるため、データベースやセッションの値に基づいてフォーム定義を設定することが可能です。
また、このメソッドが呼び出されたあと、Ethna_ActionForm#setFormVars が呼び出されるため、上記で設定したフォーム定義に基づいて submit した値が自動設定されます。
/** * フォーム定義変更用、ユーザ定義ヘルパメソッド * * Ethna_ActionForm#prepare() が実行される前に * ユーザが動的にフォーム定義を変更したい場合に * このメソッドをオーバーライドします。 * * $this->backend も初期化済みのため、DBやセッション * の値に基づいてフォーム定義を変更することができます。 * * @access public */ function setFormDef_PreHelper() { // セッションや DBのオブジェクトを以下のようにして利用可能 $session = $this->backend->getSession(); $db = $this->backend->getDB(); // フォーム名とフォーム定義の設定 $name = "question_1"; $def = array ( 'name' => $name . "の答え", // 動的なフォーム定義 'required' => true, 'form_type' => FORM_TYPE_TEXT, 'type' => VAR_TYPE_STRING, 'filter' => null, ); $this->setDef($name, $def); // このメソッド呼び出しのあと、Ethna_ActionForm#setFormVars // が呼び出され、上記で追加した question_1 の値も // 自動で設定される。 }
フォームヘルパが絡んだ場合
フォームヘルパについては、フォームヘルパ の説明も参照してください。
少し高度な話題ですが、フォームヘルパは、Ethna_ViewClass で現在のフォームとは別に、以下のように ethna_action で指定されたアクションフォームを初期化します。それは、Submit されたときに初期化されたアクションフォームとは別のため、動的に値を設定したい場合は別のAPI が必要になります。
{form ethna_action="formhelper"} {* [appid]Form_Formhelper というアクションフォームが初期化される *} {/form}
フォームヘルパで利用するフォーム定義を動的に変更したい場合は、以下の特別なメソッドを使います。使い方は フォーム定義変更専用のヘルパメソッド で説明した setFormDef_PreHelper() と全く同じです。
/** * フォーム定義変更用、ユーザ定義ヘルパメソッド * * フォームヘルパを使うときに、フォーム定義を動的に * 変更したい場合に、このメソッドをオーバライドします。 * * このメソッドは、以下の定義をテンプレートで行った場 * 合に呼び出されます。 * * {form ethna_action=...} (ethna_action がない場合は呼び出されません) * {form_input action=...} (action がない場合は呼び出されません) * * @access public */ function setFormDef_ViewHelper() { // TODO: デフォルト実装は Ethna_ActionClass#prepare の直前 // に呼び出されるものと同じ。異なる場合にオーバライドする $this->setFormDef_PreHelper(); }
Ethna 2.3.5 以前のやり方
どこに書くか?
動的な定義も含めてActionFormの中で完結させたいところですが、ActionForm 内にはロジックが書けないため、データベースのインスタンスを拾うなどしてフォーム定義を動的に変更することができません。
Ethna_ActionClass#prepare もしくは Ethna_ActionClass#perform に処理を記述すればよいですが、フォーム値の自動検証を Ethna_ActionClass#prepare で行うのが一般的であることを考えると、前処理を行う prepareに処理を書いたほうがスマートです。
フォーム定義の設定
まず、フォーム名とその定義がどのようなものになるかを決めます。以下の例では、フォーム定義 $def に "$name の答え" という動的な値を定義しています。
// フォーム名とフォーム定義の設定 $name = "question_1"; $def = array ( 'name' => $name . "の答え", // 動的なフォーム定義 'required' => true, 'form_type' => FORM_TYPE_TEXT, 'type' => VAR_TYPE_STRING 'filter' => null, );
しかしActionFormのインスタンスにこの定義を設定しない限り、このフォーム定義は使用できません。
よって以下のようにして、Ethna_ActionForm#setDef メソッドを呼び出してフォーム定義を設定します。
複数のフォーム定義を処理するには、この処理をループさせればよいでしょう。
// フォーム定義をセットする $this->af->setDef($name, $def);
submitされた値を設定しなおす
Ethna_ActionForm#setDef を呼び出しただけでは、アクションフォーム内のフォーム定義が変更されただけで、submit された値がアクションフォームに設定されたわけではありません。
よって、以下のようにして $_POST や $_GET 等の値をアクションフォームのインスタンスに再設定してやります。
// submit された $_POST 等の値をアクションフォーム // に設定しなおす $this->af->setFormVars();
こうすれば、新しいフォーム定義に基づいて検証を行うことができます。
// 新しいフォーム定義に基づいてフォーム値を検証 if ($this->af->validate() > 0) { return 'index'; }
全体のサンプルコード
上記の説明で使ったサンプルコードの全容をまとめると、以下のようになります (左側は行番号です)
1: function prepare() { 2: 3: // フォーム名とフォーム定義の設定 4: $name = "question_1"; 5: $def = array ( 6: 'name' => $name . "の答え", // 動的なフォーム定義 7: 'required' => true, 8: 'form_type' => FORM_TYPE_TEXT, 9: 'type' => VAR_TYPE_STRING 10: 'filter' => null, 11: ); 12: 13: // フォーム定義をセットする 14: $this->af->setDef($name, $def); 15: 16: // submit された $_POST 等の値をアクションフォーム 17: // に設定しなおす 18: $this->af->setFormVars(); 19: 20: // 新しいフォーム定義に基づいてフォーム値を検証 21: if ($this->af->validate() > 0) { 22: return 'index'; 23: } 24:}
(参考)ActionFormのform定義を直接変更する方法
別のやり方として、prepare内で、$this->af->formを直接変更するやり方もあります。
http://ethna.jp/ethna-document-faq-dev_guide_faq.html#jee57430
*1 (クラスのメンバ変数が固定でなければならない根拠としては、PHP4では、http://jp.php.net/manual/ja/keyword.class.php が、PHP5 では、http://jp.php.net/manual/ja/language.oop5.basic.php があります。