Zend Framework - Zend_Form編
HOME |
TOP
■ Zend_Form概要
Zend_Formは、直接フォームをHTMLタグ打ちすることなく、パラメータを設定することでフォーム作成や入力値チェック(バリデータ Validator)を行えるようにするコンポーネント(クラス)です。
つまり、設定するパラメータにはフォーム作成に必要な値と入力値チェックに必要な値があることになります。
設定できるパラメータはこれら以外にも、フォーム作成と関わりの深い見た目を変えるためのデコレータ(Decorator)や入力値チェックを通過した値を加工できるようにするためのフィルター(Filter)があります。
フォームを作成するためにはまず、フォームオブジェクトを作成します。
作成したフォームオブジェクトに各種パラメータ(フォーム作成値・バリデータ値・フィルター値・デコレータ値)を与えることで実際にフォームを作成したり、送られてきた入力値のチェックに利用したりすることができるようになります。
つまりフォーム作成だけにZend_Formを使うくらいなら直接フォームタグを書いた方がいいと思います。フォーム作成以外に、送られてきた入力値のチェックでも使ってこそZend_Formを使う意味が出てくると思います。
▽ フォームの外形
ここで少しだけフォームの外形に触れておきます。
まず『<form> 〜 </form>』の一組のフォーム全体を単にフォームと呼びます。
このフォーム全体の持つパラメータ値は、ちょうど<form>タグの持つ属性("action"や"method"など)と一致します。
そしてフォームの内部にはフォーム要素(formElements)と呼ばれるさまざまな部品(テキストボックスやsubmitボタンなど)が属してることが分かります。
▽ フォーム作成の流れ
[1] フォームを作成するためにはまずフォームオブジェクトを作成します。
これは単にZend_Formクラスからオブジェクト作成する(つまりnew演算子を使う)だけで作ることができます。
$form = new Zend_Form();
これで空っぽの『<form> 〜 </form>』が一組作成されたイメージになります。
[2] フォームオブジェクトが作成できたらこのオブジェクトにフォーム作成に必要なパラメータ(主にフォーム要素作成のためのパラメータで、他にも入力値チェック用のパラメータなど)を与えていくことになります。
各種パラメータを与えたらフォームは完成形です。あとはフォーム作成でも入力値チェックでもできる状態です。
パラメータを与えてフォームを完成させていくやり方はいろんな方法が用意されているので別記(次項: フォーム要素の作成と登録)します。
[3] render()メソッドを使ってレンダリング(フォームの描画)を行います。
echo $form->render();
レンダリング結果を変数で受け取ったりechoで表示させたりできます。
▽ フォーム入力値チェックの流れ
フォーム作成の流れで説明した[2]まででフォームが完成しています。
あとはこれを使って、送られてきたフォーム入力値をチェックすることになります。
チェックしてほしい値をisValid()メソッドに渡すと、設定した期待どおりの値かどうかを真偽値で返してくれます。
ここのチェックを通過すれば次に行いたい処理へ進めることになります。
■ フォーム要素の作成と登録
フォームオブジェクトを作成(new演算子を使う)できたら次はそこにフォーム要素を加えていく(各種パラメータを設定する)作業になります。
それに先駆けて、まず手始めにシンプルなもの(フォーム要素を持たない空っぽのフォーム)を表示させてみることにします。
空っぽのフォームオブジェクトのままフォーム作成してみるスクリプト
実際にレンダリングされた結果のHTMLソース
まだこれだけのタグなので当然画面上は何も無い真っ白けです。
これを見ておいおいおいと、なんだこれはと思われた方もいらっしゃると思いますが、役割り分担がはっきりしている(きっちり分担することでバグの入り込む余地を少なくしている)Zend Framework(のみならずオブジェクト指向)では当然のことながら描画はZend_Viewが担当することになります。
なのでZend_Formを単独で使う場合にはsetView()メソッドに個別にビューオブジェクトを登録する必要がありますが、MVCアプリケーションではビューオブジェクトの登録は自動的に行われるのでこの登録作業は不要です。
▽ 各種パラメータ
フォームを完成させていくためのパラメータは大きく2種類に分けられます。
1つはフォーム全体として持ってる値("action"や"method"など)で、もう1つは個々のフォーム要素の持つ値です。
フォーム全体として持ってる値を登録するのは簡単です。
作成済みのフォームオブジェクトに専用メソッドで渡すか、フォームオブジェクト作成時に渡すことができます。
[1] 専用メソッドを使う方法
$form->setAction('/foo/bar')->getMethod('post');のようにメソッドチェーンでも渡せます。
[2] フォームオブジェクト作成時に渡す方法
フォームオブジェクト作成時にはフォーム要素関連の値も渡すことができるので、この方法を使うとフォームオブジェクト作成と同時に一気にフォームを完成させることができます。
また、階層構造の配列を渡しているだけなのでiniファイルに階層構造を記述してZend_Config_Iniで配列化したものを渡して一気に作成することもできます。
フォーム全体として持ってる値の登録はこれだけです。あとは個々のフォーム要素に持たせる各種パラメータを登録していくことになります。
フォーム要素に持たせるパラメータは、フォーム要素のオブジェクトを作成して登録する方法と、フォームオブジェクト作成時に配列を渡して作成と登録を一度に行う方法とに分けることができます。
[1] 作成して登録する方法
この段階ではフォーム要素<input type="text" name="username">のようなものが作成できる程度のパラメータが渡せたイメージになります。
上記createElement()メソッドを使った作成と似たような方法として、専用クラスを使ってインスタンス化する方法もあります。
こちらの方法だとクラス呼び出しの記述まで増えてしまうのであまり使われない方法でしょう。
[2] 作成と登録を一度に行う方法
この方法では"action"や"method"はもちろんのこと、フォームの持つフォーム要素に関するパラメータをすべて指定した配列を渡します。
このようにインデントして階層構造を分かりやすくした方がよりフォームが明確な感じで見通しがよくなります。
これと似たような手法(フォームオブジェクト作成時に一気にパラメータ渡し)として、iniファイルにパラメータ設定を書いておき、これを配列にセットするやり方があります。
このiniファイル設定値は以下のようにして配列化できるのでそのまま渡せます。
この手法は他でも用いるので覚えておいた方がいい手法です。
直書きで配列を直接渡す方法は、フォーム数が割と少ない小規模アプリケーションでは有効な感じですが、フォーム数が多くなってくると徐々に見通しが悪くなってきます。
そういう意味でもこのiniファイルの手法は覚えておきたいものです。
フォーム要素に設定できるパラメータにはいくつか種類があります。
- フォーム要素作成(タグ生成に必要な属性値)
- フォームの表示を変えるデコレータ(Decorator)
- 入力値チェックのためのバリデータ(Validator)
- 入力値加工のためのフィルター(Filter)
設定できるパラメータの種類と数が多いので、実際に作成していく中で必要なものだけを覚えるしかないでしょう。
ということでフォーム要素のパラメータ指定はいろいろあるので、有効だと思えるものに絞って書いていきます。
具体例として、よくあるログインフォームで考えていきます。
ログインフォームでは、入力できるフォーム要素として「テキストボックス」と「パスワード」の2つがあります。
あとは送信ボタンがあるくらいでしょう。
ログインフォームのイメージ
まず「なまえ」に使用できる文字種類が決まっているはずです。
加えて少なくとも6文字で最大で20文字長などの仕様もあるはずです。
「パスワード」にも同様のことが言えます。
これら「なまえ」と「パスワード」は共に必須入力になります。
「送信」ボタンは送信の文字を設定するだけで済みそうです。
ではログインフォームから入力されるべき値、期待する値をパラメータ指定にしていきます。
見てくれを変えるデコレータまで書くと記述が増えて混乱するので、まずはデコレータはいじらずにデフォルトを使うとして、あとのパラメータをiniファイルに具現化していきます。
iniファイルでは、[foobarfuga]の形式で記述するとfoobarfugaセクションが出来上がります。
iniファイルを取り込む時にセクション指定できるので複数フォームがあるならセクション分けしていくと分かりやすくなるでしょう。
先頭を"form"としていますが、これは取り込んで配列渡しするときに$config->formで指定できるようにするためのものです。なくても問題ないものですが、内容が分かりやすくなるフレーズがいいでしょう。
では設定したパラメータを見ていきましょう。
これだけ整列していると見ればなんとなく分かると思います。
まず任意で付けた頭の"form"をはずして考えると
elements.{name属性値}.type = フォーム要素のタイプ指定
elements.{name属性値}.options.label = ラベル指定
ということが分かります。
ラベルというのは普通はフォーム要素のすぐ左にある「なまえ」や「パスワード」といった表示文字になります。submitボタンの場合はボタン上の文字はvalueではなくlabelで設定するようです。
デコレータはあとで説明するので、ここまででフォーム作成(タグ描画)に関わるパラメータは設定完了ということになります。次は、
elements.{name属性値}.options.required = true
という行がありますが、これは必須入力であることを意味します。なので空っぽだと入力エラーの判断をすることになります。
あとは、バリデータの指定があります。(前述のrequiredも一応はバリデータです)
elements.{name属性値}.options.validators.alnum.validator = "alnum"
elements.{name属性値}.options.validators.regex.validator = "regex"
elements.{name属性値}.options.validators.regex.options.pattern = "/^[a-z]/i"
elements.{name属性値}.options.validators.strlen.validator = "StringLength"
elements.{name属性値}.options.validators.strlen.options.min = "6"
elements.{name属性値}.options.validators.strlen.options.max = "20"
"alnum"というのは、「アルファベット+ナンバー」なので英数字のみを許可することになります。
それから"regex"がありますが、これは正規表現によるチェックを行います。その内容が"/^[a-z]/i"になっているので、先頭文字が英字であるかチェックすることになります。
あと文字列長が6〜20文字の範囲かどうかもチェックしていることになります。バリデータ指定は以上です。
そしてフィルターの指定があります。
elements.{name属性値}.options.filters.lower.filter = "StringToLower"
これは入力値のチェックを無事に通過したものをgetValue()メソッドで取得する時に施す加工です。「なまえ」として入力された英字は取得段階でフィルターによってすべて小文字変換する指定です。
この方法で取得すればもう英小文字であることが保証されていることになるので、個別にチェックや加工処理をすること無しに次の処理へ進むことができるということになります。
■ デコレータ
今度はデコレータに重点を置いて、フォームのいろいろな見せ方を考えていきます。
設定をより見やすくするために、前項で書いたバリデータ・フィルター指定の部分はそぎ落とします。
この項はほとんどわたしの勘で書いたものです。つまり間違ってる可能性が高いです。
というのも、わたしは技術書や解説本のたぐいは一切読まずにネット情報だけをかき集めて覚えたつもりになって我流の説明をしているからです。
そんな中でもZend_Formのデコレータの解説サイトがなかなか探しきれずにいます。
頼みの公式リファレンスでも見つけられません。
いくら検索しても判で押したような記述のサイトばかりでしかも表示が重いです。なんか泣けてきます。><;
まずはデコレータ指定の基本を押さえておきます。
デコレータは設定しないでもデフォルトで
といったタグが施されて構造化された表示になります。
つまりこの構造化している<dl>, <dt>, <dd>といったタグは、デフォルトのデコレータによって施されているということになります。
上記はフォーム要素が2つある場合ですが、このデフォルトのデコレータ指定はiniファイルに記述するとだいたい次のような感じになります。
; フォーム全体のデコレータ
decorators.elements.decorator = "FormElements"
decorators.dl.decorator = "HtmlTag"
decorators.dl.options.tag = "dl"
decorators.form.decorator = "Form"
; 各フォーム要素のデコレータをまとめて設定
elementDecorators.helper = "ViewHelper"
elementDecorators.dd.decorator = "HtmlTag"
elementDecorators.dd.options.tag = "dd"
elementDecorators.dt.decorator = "Label"
elementDecorators.dt.options.tag = "dt"
代表的なデコレータ
デコレータ名 | 説明 |
FormElements | 各フォーム要素の描画結果を結合して返す |
ViewHelper | ビューヘルパを利用してフォーム要素を描画 |
HtmlTag | tagで指定したタグで囲む |
Form | <form>, </form>タグを描画する |
Label | フォーム要素にラベルがあれば描画する。tagを指定できる |
Errors | 入力エラーがあった場合にフォーム上にエラー表示させるためのもの |
tag指定できるデコレータは"HtmlTag"と"Label"の2つです。(多分)
デコレート範囲 | 先頭ワード | 任意の名前空間 | 指定部分 |
フォーム全体 | decorators | elements,formなど 自由に付ける | decorator = "デコレータ名" options.tag = "指定タグ名" |
フォーム要素まとめて | elementDecorators |
フォーム要素を個別に | elements.{name値}.options.decorators |
先頭ワードでデコレート範囲が決まります。
先頭ワードの次にくっつくのは任意の名前空間で、最後に指定部分がくっついた形です。
なぜか"FormElements"と"ViewHelper"は、decorator = のdecorator部分を省略できる。(自動的に呼ばれてるから?)
では、実際にデコレータを使ってフォームを作ってみましょう。
▽ デコレータを指定しない状態(デフォルト)
[./form.ini]
フォーム作成用スクリプト
フォームのレンダリング結果
なんか物足りないけど、デフォルトのデコレータではこんな感じになりました。
レンダリングされたフォームのHTMLソース
やはりフォーム要素が増えるほど<dl>, <dt>, <dd>タグがまとわりつく感じになっております。
▽ デコレータによって付加されているタグをすべて取り除いてみる
先ほどは<dl>, <dt>, <dd>タグがまとわり付いた感じになりましたので、今度は全部取っ払ってみます。
注目すべきはdisableLoadDefaultDecoratorsをtrue指定しているところです。
フォームのレンダリング結果
レンダリングされたフォームのHTMLソース
なんとか希望通りに取り除くことができました。
▽ 全フォーム要素をまとめてデコレートする(その1)
全フォーム要素をまとめて指定するにはelementDecoratorsを使います。
フォームのレンダリング結果
ラベル文字を太字にしてみました。なくはないですかね
ボタンのラベルがボタン上の他にも出現しました。全フォーム要素まとめてラベルを<b>で囲む指定にしたので当然ですね。
レンダリングされたフォームのHTMLソース
▽ 全フォーム要素をまとめてデコレートする(その2)
今度は難しそうなtableタグを施すデコレータに挑戦してみます。
フォームのレンダリング結果
これはイケたんじゃないでしょうか? これはアリですな。これのやり方が載ってるサイトがなかなか見つからなくて結構苦労しました。
ただ、人間の欲望は際限がありません。できることならボタンちゃんだけはtableタグの外に出したい!
レンダリングされたフォームのHTMLソース
▽ フォーム要素を個々にデコレートする
先ほどはボタンまでもがtableタグに含まれてしまいました。なんとか外に出すように頑張ってみます。
ということで、今度はフォーム要素を個別にデコレート設定する必要がありそうです。
とは言っても、tableタグの開始と終了を指定するやり方はどこを探しても見つけられませんでした。
自称めちゃくちゃ検索には自信があるんですが探しきれませんでした。
なので自力でなんとかしましょう。公式リファレンス等には、グループ表示というのができるというのはチラっと載っているのでそれいただきです。
tableタグに含ませたいものだけをグループ化すればよさそう。ということで、displayGroupsでグループ定義し、displayGroupDecoratorsでグループのデコレータとしてtableタグを設定しました。
フォームのレンダリング結果
これです。期待通りのものができました。かなり悩みましたけど。
最初はボタン位置がtableの上に来るようになってしまったので、order指定も加えることになりました。
にしても検索しても見つけられないってことはもしかしてZend_Formってそんなに使われていないのが実状なのかな? 実はZend Frameworkのことそんなに知らないんですよね。。
薄々は思っていたけど、フォーム設定だけで結構冗長というか、、、ね。
レンダリングされたフォームのHTMLソース
▽ 多少見栄えをよくする努力をしてみる
本来ならスタイルシートの指定値はタグ埋め込みではなく、一箇所にくくり出してタグではclassやid指定にしたいところですが、シンプル例ということで。。。
フォームのレンダリング結果
これはキテるね、ハッキリ言って。。バキバキきてる
レンダリングされたフォームのHTMLソース
▽ もっとコンパクトに書いてみる
前回までは、フォーム要素"username"と"password"別々に同様のデコレータ設定を施すやり方をしました。
この方法だと、フォーム要素が増えれば増えるほど記述がかさばるのは目に見えています。
そこで、おのおののフォーム要素をまとめて設定できるelementDecoratorsがあるわけですからこれを使ってコンパクト化してみます。
ただし、例によってsubmitボタンまで同様のデコレートが施されては意味がなくなりますので、一旦elementDecoratorsでデコレートした後でsubmitボタンだけデコレートを無効化する方法を考えます。
公式リファレンスを眺めていたら、removeDecoratorというそれっぽい名前のものがありました。しかし残念なことにiniファイルにどう記述したら有効になるのかわかりませんでした。
そこで考えた方法は、全フォーム要素デコレート後にsubmitボタンだけ"ViewHelper"デコレータを呼び出してそれまでのデコレートをリセットするやり方です。これは正統なやり方なのかはわかりませんが、これでできたので書いておきます。
フォームのレンダリング結果
これでできました。いいんじゃないでしょうか
レンダリングされたフォームのHTMLソース
▽ ついでにもう一つ考えてみました