トップ «前の日記(2006-03-08) 最新 次の日記(2006-03-10)» 編集

2002|01|02|03|04|05|06|07|08|11|12|
2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|02|03|04|07|

2006-03-09 [長年日記]

_ 次世代のフォーム処理 その3

ZFormが使っていたアプローチをHTML_QuickFormに適用できないか、試しに検討。HTML_QuickForm関連クラス自体を書き直すのではなく、外付けで拡張する方法で。

外付けで拡張するとなると、HTML_QuickForm_Elementたちをいちいち拡張するわけにはいかない*1から、できるのはせいぜいHTML_QuickForm本体を拡張したクラスを作って、従来の各関係クラスの機能やインターフェースとの互換性を確保したまま、Ajax対応の仕組みを追加するアプローチだろう。

HTML_QuickForm_Rendererを拡張しちゃえば、出力部分はかなり大幅にいじれるけど、Rendererは基底クラスは単なるインターフェース定義で、実装は各派生クラスでやっちゃってるから、一カ所直せば済むって話にはならなそうだ。手を出さない方が無難だろう*2

となると、HTML_QuickFormにAjax関連の設定を追加するためのメソッドを追加し、HTML_QuickForm::acceptあたりを互換性を持ったまま書き換えて、追加されたAjax関連の設定を見ながら、必要なJavaScriptコードを吐き出す感じかなー。その際には各elementがIDを持っていない場合は、IDを振る仕組みとかも必要そうだ。イベントハンドラー自体は直接element出力時に出力しなくても、IDさえ分かるなら後付けでEvent.observeできる。

というわけで、まとめてみる。

  • HTML_QuickFormを拡張したHTML_QuickForm_Exを作る。$form =& new HTML_QuickForm()の部分を$form =& new HTML_QuickForm_Ex()に変えるだけで、従来のHTML_QuickFormを使っていたコードは完全に動作するようにする。
  • HTML_QuickForm_Ex::addAjaxHandler(mixed $element, string $event, mixed $callback, array $ajaxOptions)を追加し、各element($element == __ALL__の場合はform自体)にAjax拡張用の情報を追加する。
    • コールバックの実行をXmlHttpRequestがonCompleteな場合のみ対応するのならば、このようなメソッドでいいけど、コールバックのパターンを増やしたいならば、インターフェースをもっと練る必要がある
    • HTML_QuickForm_Ex::removeAjaxHandler(mixed $element, string $event, mixed callback)も用意しておいた方がいいだろう
  • HTML_QuickForm_Ex::accept(&$renderer)で、HTML_QuickForm::accept相当の機能を実行した後に、必要なJavaScriptコードを出力する機能を追加する
    • Renderer_Defaultの場合は、$renderer->_hiddenHtmlにJavaScriptコードを追加すればいいだろう(JavaScriptの実行順序を考えると、$renderer->_htmlにコードを追加する必要があるかも。いや、$render->_hiddenHtmlにすべて出力した上で、onloadで初期化コードを呼ぶようにすれば問題ないかな?)。
    • toArrayなRendererの場合は、$renderer->_ary['javascript']にJavaScriptコードを追加すればいいかな。初期化周りの問題は上と同じ。
    • toObjectは使ったことないんで(Flexyは使ってない)よくわからないけど、多分toArrayな場合と同様に、$renderer->_obj->javascriptにJavaScriptコードを追加すればいいんだよね?
    • その他のRendererは使わないんでパス。JavaScriptコードさえ生成できていれば、出力を追加するのは簡単だろう。
  • その他懸案事項
    • Rendererに割り込むあたりは、外部オブジェクトのプライベート変数を外からばりばり書き換えるアプローチなんで、将来の互換性は厳しい。
    • javascript側のコードを書いて、考えていたような処理がJavaScriptでできるかどうか試してみないとな。まだ単純なprototype.jsのテストコードしか書いたことがない。
    • HTML_QuickFormが出力するJavaScriptバリデーションコードと、主に実行順序的にバッティングしないかどうか検証が必要。確かあれはformのonsubmitに直接イベントハンドラーを書いていたはずだけど、prototype.jsでEvent.observeした場合は、どういう実行順序になるんだろう? あるいは上書き?
    • そういやコールバックをどうやって記述するか全然考えてない。多分普段はSmartyと組み合わせて使うだろうから、Smartyテンプレート側に記述するのが無難? あるいはコールバック関数の内容自体もここで登録できるようにする方がいい?
    • elementにIDがない場合にIDを振る処理は、acceptの最初にやるべきか。自動生成したUNIQUE PREFIX+element名とかにIDを固定しちゃった方が扱いは楽なんだけどな。
    • どうせprototype.jsを使うんだったら、Ajax.Updaterとかも使いたくなりそうだけど、仕組みが複雑になりすぎるかな?

_ 次世代のフォーム処理 その4

いや、よく考えたらこの仕組みはフォームに限定する必要がまったくないな。基本的にDOM要素のIDとイベント名が分かれば、イベントハンドラーの登録はできるわけで、フォーム生成ライブラリと連携しなければならない必然性は非常に薄い。

対象のDOM要素がフォームまたはフォーム要素じゃない場合は、XmlHttpRequestで送るパラメータを何にするかが未確定だけど、逆に言うと、渡されたDOM要素の種類がフォームまたはフォーム要素ならば、送るパラメータを自動で特定できるわけだ。

となってくると、HTML_QuickFormを拡張するとか考えずに、独立したライブラリとしてHTML_AjaxHandlerとかを作った方がいいかもしれない。

class HTML_AjaxHandler
{
    var $_handlers = array();

    function addHandler($domId, $eventName, $callbacks, $ajaxOptions)
    {}
    
    function removeHandler($domId, $eventName, $callbacks)
    {}
    
    function toJavaScript()
    {}
}

とかで十分いけるかな? この程度だったら、コールバックのタイミングを複数対応にしたりしても、全体としてはさほど複雑にならないだろうから、その辺までやっちゃえるか。

*1 HTML_QuickForm経由の自動生成ではなく、HTML_QuickForm_Elementを直接newする使い方も珍しくないから、Elementレベルで拡張してしまうと互換性的に厳しい

*2 と書いてからRenderer周辺を読み直してみたら、やっぱりRendererの各実装は(直接Rendererを書き換えないまでも)意識しないわけにいかないっぽい。toHtml、toArray、toObjectの3パターンを意識しておけばいいのかな?