この文書は、PEAR general メーリングリスト に投稿された質問を基にして作成されています。 メーリングリストのアーカイブを検索すると、 より詳細な回答や例が見つけられるでしょう。
<form>
タグの action
属性を変更する方法は?
HTML_QuickForm_Page のコンストラクタで
action
属性を設定できないようになっている唯一の理由は、
間違って自分の足を撃ちぬいてしまうようなミスをしないようにすることです。
どうしても属性を変更したければ、HTML_QuickForm_Page クラスの
setAttribute() メソッドあるいは updateAttributes()
メソッドを使用します (これらは HTML_Common
から継承したものです。このクラスの API についても知っておく必要があります)。
混乱を避けるため、ここでは アクションの名前を表す文字列のことを「アクション」、 HTML_QuickForm_Action のサブクラスのことを「アクションハンドラ」と表記します。
独自のアクション名を使用してそのハンドラを作成することも可能ですが、 まずはデフォルトのアクション名およびそのハンドラを理解する必要があります。
'display'
このアクションのデフォルトハンドラは HTML_QuickForm_Action_Display で、これはデフォルトのレンダラを使用してフォームを表示します。 出力内容を変更したい場合は、このハンドラのサブクラスを 作成する必要があります。 このアクションは、ページを表示する必要がある際に自動的にコールされます。 getButtonName() でボタンに関連付けられるべきでは ありません。
'submit'
このアクションは、フォームの "グローバル" な送信ボタンに
関連付けなければなりません。単一ページのフォームや
タブ形式の複数ページフォームでは "submit" ボタン、
あるいはウィザードでは "finish" ボタンに
関連付けることができます。このアクションのデフォルトハンドラは
HTML_QuickForm_Action_Submit
で、フォームの全ページの入力内容を検証したあとで、
'process'
ハンドラをコールするか
無効な入力用のページを表示します。
'next'
このアクションは、(通常は) モーダルな複数ページフォーム (ウィザード)
の "Next" ボタンに関連付けなければなりません。
このアクションのデフォルトハンドラは
HTML_QuickForm_Action_Next
で、現在のページの入力内容の妥当性を検証した後で、もし内容に問題がなければ
(あるいはフォームがモーダルでなければ) 次のページにリダイレクトします。
モーダルな複数ページフォームの最後のページにいる場合は、
デフォルトの 'submit'
ハンドラと同じように動作します。
'back'
このアクションは、(通常は) モーダルな複数ページフォーム (ウィザード)
の "Back" ボタンに関連付けなければなりません。
このアクションのデフォルトハンドラは
HTML_QuickForm_Action_Back
で、前のページが存在する場合にそのページにリダイレクトします。
QFC 0.9.3 以降では、'back'
アクションの際にはフォームの検証は行われなくなりました。
'jump'
このアクションのデフォルトハンドラは HTML_QuickForm_Action_Jump で、単にフォーム内の指定したページへの HTTP リダイレクトを行います。 このアクションは getButtonName() でボタンに関連付けられるべきでは ありません。
'process'
このアクションは、デフォルトの 'submit'
および 'next'
(ウィザードの
最終ページの場合のみ) のハンドラとして、
フォームの送信処理が成功した (つまり、入力にエラーがなかった) 際に
コールされます。このアクションにはデフォルトのハンドラが存在しません。
独自のハンドラを定義して、フォームの値を処理するために必要な
すべてのロジックを実装する必要があります。
これ以外にも
HTML_QuickForm_Action_Direct
というデフォルトのハンドラがあり、これはデフォルトのアクションを
持っていません。
これはフォームの指定したページに移動する際に使用され、
Page::addAction()
あるいは
Controller::addAction()
を使用して明示的に追加しなければなりません。その際には、
移動先のページの名前を $actionName
に指定し、getButtonName()
を使用して同じ名前でボタンにバインドします。
Page の handle() メソッドを使用しなければなりません。
<?php
$page->handle('action');
?>
Controller の handle() メソッドは、必要に応じて自動的にコールされます。
HTML_QuickForm_Action のサブクラスを作成し、必要な処理を perform() メソッドに追加します。作成したクラスを、addAction() で適切な名前を指定して Page あるいは Controller に追加します。
getButtonName() でこのアクションを何らかのボタンに バインドしたい場合は、 container() の中の値を保存することに注意しなければなりません。
フォームを作成する。
コンテナへの参照を取得する。
コンテナの ['values'][$pageName]
および ['valid'][$pageName]
要素を指定する。
いつものように、 使用例はデフォルトのアクションハンドラのソースを参照ください。
コントローラは、<input type="image" />
コントロールにバインドしたアクションも正しく処理することができます。
何も特別なことをする必要はなく、単に getButtonName()
でコントロールの名前を指定すればよいのです。
ハイパーリンクなどにアクションをバインドしたい場合には、 次のことを考慮する必要があります。フォームの値を取得するためには、 フォームを送信しなけらばなりません。 そのため、javascript などでフォームを送信する処理を記述し、 何らかの方法でコントローラにアクション名を渡す必要があります。
コントローラは、フォームのデータや検証状態を、 セッション内のコンテナに保持します。このコンテナには container() メソッドを使用してアクセスできます。 フォームをリセットするためには、このコンテナを消去する必要があります。 そのためには、container() に TRUE を渡します。
答えはきわめて簡単です。HTML_QuickForm_Controller の container() メソッドを使用して、このようにしなければなりません。
<?php
// 参照であることに注意
$data =& $page->controller->container();
$data['_my_stuff'] = $stuff;
?>
受け取り側のページでは、以下のようにします。
<?php
$data =& $page->controller->container();
$stuff = $data['_my_stuff'];
?>
QFC の将来のバージョンで名前の衝突が起こることを避けるため、 あなたが作成するフィールドには独自のプレフィックスを使用するか、 あるいはアンダースコアで始まる名前を使用してください。
Controller は、あなたが追加したデータについては 何も知らないことに注意しましょう。 exportValues() メソッドや類似のメソッドでこのデータが返されることを期待しないでください。 container() メソッドを使用し、自分でデータを抽出しなければなりません。 しかし、コンテナがリセットされる際には あなたが追加したデータも削除されます。
まず最初に、HTML_QuickForm_Page
が HTML_QuickForm のサブクラスであること、
そのため親クラスの変更内容はすべて子クラスにも適用されるということを
理解してください。
HTML_QuickForm の
レンダラについての節 および使用したいレンダラのドキュメントを
読むことをお勧めします。QuickForm 配布物の
docs/renderers/
ディレクトリの中に、使用例があります。
すでに説明したとおり、もしページの出力内容をカスタマイズして
ページの 'display'
アクションの
ハンドラとしてこのサブクラスのインスタンスを追加したいのなら、
HTML_QuickForm_Action_Display
クラスのサブクラスを作成しなければなりません。
レンダラ固有のコードは、すべてその
_renderForm() に含めます。
Controller によって発生する唯一の新しい問題は、 getButtonName() を使用してアクションにバインドしたボタンです。 動的なレンダラを使用している場合は、これは問題にはなりません。 というのも、要素の名前に気を使う必要がないからです。しかし、 静的なレンダラを使用してフォームを出力する場合には、 要素の名前を知っておく必要があります。
ここでは ITStatic レンダラを使用すると仮定しますが、 それ以外の静的レンダラでも同じようになります。 基本的に、3 つの選択肢があります。
getButtonName() で自動生成された名前を使用して、ボタン/プレースホルダを フォームに手動で追加します。この方法は 推奨されません。なぜなら、名前を気にするのは Controller 自身のみであるべきで、 直接使用すべきものではないからです。
自動的に命名されたボタンを
グループ
に追加します。グループにはお好みの名前をつけることができますが、
$appendName
を false
に設定することを忘れないでください。
ITStatic は、プレースホルダ {form_element_html}
を発見すると、グループの HTML をそこに代入します。
テンプレートの中から getButtonName() をコールします。 テンプレートに次の内容を追加し、
HTML_QuickForm_Action_Display のサブクラスの _renderForm() メソッドに次の内容を追加します。
<?php
$tpl->setCallbackFunction('buttonName', array(&$page, 'getButtonName'));
?>
このコードは HTML_Template_Sigma で動作します。 HTML_Template_ITX (X に注意) の場合はもう少し調整しなければなりません。
この質問に貢献してくれた Donald Lobo に感謝します。
3 つのページからなるウィザードがあるとしましょう。
QFC のデフォルトの HTML_QuickForm_Action_Next および HTML_QuickForm_Action_Back に基づいたアクションハンドラを作成する必要があります。 これは、ページレベルおよびコントローラレベルのどちらでも行うことが可能です。 ページレベルでサブクラスを作成するほうがシンプルですが、 複雑な StateMachine を使用するなら複数のサブクラスを作成することになります。
上の例の場合、ページ 1 の 'next'
アクションは、
ユーザの入力内容に応じてページ 2 あるいはページ 3 のいずれかに
'user' を送信します。このハンドラは、訪れなかったほうのページに
大しても 'valid'
フラグを設定します。
ページ 2A および 2B の 'back'
アクションは、
ユーザをページ 1 に戻します。一方、'next'
アクションはページ 3 に移動します。
最後に、ページ 3 の 'back'
アクションハンドラは、
ページ 1 での入力内容に応じてページ 2A あるいは 2B のいずれかに移動します。
実際に動作する例は、statemachine.php
ファイルです。
'Back'
ボタンの場合に検証をしないようにするにはどうしたらいいですか?
クライアント側の検証は、フォームの onSubmit
ハンドラからコールされます。このハンドラを削除すれば、検証は実行されません。
つまり、ユーザが 'Back'
ボタンを押した際に
このハンドラを削除しなければならないということです。例えば
ボタンの onClick
ハンドラに以下のように記述すれば、
これが実現できます。
this.form.onsubmit = null; return true;