ここは以前の ethna.jp サイトを表示したものです。ここにあるドキュメントはバージョン2.6以降更新されません。
最新のドキュメントは 現在のethna.jp を閲覧してください。現ドキュメントが整備されるまでは、ここを閲覧してください。
Ethna > ドキュメント > 開発マニュアル > ethna-document-dev_guide-app > フォーム定義を動的に変更する

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

Ethna でのアクションフォームの定義は、以下のように固定の配列として定義します。つまり、クラスのメンバとして宣言するため、PHP では関数コールや変数の形でフォーム定義を動的に定義することはできません。これは PHP の言語仕様上の制限です*1

ここでは、そうした制限を越えて、フォーム定義をsubmit後に変更する方法を示します。

   $form = array(
       'sample' => array(  //  正しいフォーム定義
           'type' => VAR_TYPE_INT,
           'name' => 'sample',
           // ...
       ),
       'invalid' => array(  //   間違ったフォーム定義
           'type' => $dynamic_type, // 変数は駄目
           'name' => _('invalid'),  // gettext 等、動的な呼び出しは×
           // ...
       ),
書いた人key----------新規作成
書いた人mumumu2009-01-28最新版に追随する形で全面的に修正
書いた人mumumu2009-06-03フォームヘルパが絡んだ場合の記述を追加
書いた人DQNEO2010-11-11ActionFormの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 があります。