目次

フォーム定義を動的に変更する

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を直接変更するやり方もあります。

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 があります。