最近Zend Frameworkを使い始めたんですがね、
Form要素を作る時、日本人的にはテーブルの方がしっくりくるじゃないですか?
バリデーションに続き海外製では良くあることですが、Zend_Formはデフォルトが定義リストなんです。
出力はラベルとフォーム要素のペアで、ラベルを指定しなかった場合はラベルのテキストが空白文字になります。
dtが空っぽだなんて気になるじゃないですか。私だけですか?
まあ兎に角自分好みにマークアップすべく、Zend_Formを調教してみることにしたわけです。
Default
これはクイックスタートで作るGuestBookのsignフォームです。
テキストボックスの角丸とか必須入力の赤文字とかはCSSの効果です。
ソースはコメントが無いだけでドキュメントにあるものと同じ。
applications/forms/GuestBook.php
$this->setMethod('post'); $this->addElement('text', 'title', array( 'label' => 'Title', 'required' => true, 'filters' => array('StringTrim') )); $this->addElement('text', 'name', array( 'label' => 'Name', 'required' => true, 'filters' => array('StringTrim') )); $this->addElement('text', 'email', array( 'label' => 'Email', 'required' => true, 'filters' => array('StringTrim'), 'validators' => array( 'EmailAddress', ) )); $this->addElement('textarea', 'comment', array( 'label' => 'Comment', 'required' => true, 'cols' => 50, 'rows' => 5, 'validators' => array( array('validator' => 'StringLength', 'options' => array(3, 1000)) ) )); $this->addElement('captcha', 'captcha', array( 'label' => 'Please enter the 5 letters displayed below:', 'required' => true, 'captcha' => array( 'captcha' => 'Figlet', 'wordLen' => 5, 'timeout' => 300 ) )); $this->addElement('submit', 'submit', array( 'ignore' => true, 'label' => 'Post Comment', 'class'=>'button blue' )); $this->addElement('hash', 'csrf', array( 'ignore' => true, ));
setElementDecorators
上のソースにsetElementDecoratorsを追加してフォームをデコレーションするタグを指定します。
applications/forms/GuestBook.php
$this->setDecorators(array( 'FormElements', array('decorator' => 'HtmlTag', 'options' => array('tag' => 'table', 'class' => 'zend_form')), 'Form' )); $this->setElementDecorators( array( 'ViewHelper', 'Errors', array('decorator' => 'Description', 'options' => array('tag' => 'p', 'class' => 'description')), array('decorator' => 'HtmlTag', 'options' => array('tag' => 'td')), array('decorator' => 'Label', 'options' => array('tag' => 'th')), array('decorator' => array('OuterHtmlTag' => 'HtmlTag'), 'options' => array('tag' => 'tr')), ) ); $this->setElementDecorators( array( 'ViewHelper', 'Errors', array('decorator' => 'Description', 'options' => array('tag' => 'p', 'class' => 'description')), array('decorator' => 'HtmlTag', 'options' => array('tag' => 'td')), array('decorator' => 'Label', 'options' => array('tag' => 'th', 'class'=>'hide')), array('decorator' => array('OuterHtmlTag' => 'HtmlTag'), 'options' => array('tag' => 'tr')), ), array('submit') ); $this->setElementDecorators( array( 'ViewHelper', 'Errors', array('decorator' => 'Description', 'options' => array('tag' => 'p', 'class' => 'description')), array('decorator' => 'HtmlTag', 'options' => array('tag' => 'td', 'colspan'=>2)), array('decorator' => array('OuterHtmlTag' => 'HtmlTag'), 'options' => array('tag' => 'tr','class'=>'hide')), ), array('csrf') );
こっちでもいい。
applications/forms/GuestBook.php
$this->setElementDecorators(array( 'viewHelper', 'Errors', array('Description', array('tag' => 'p', 'class' => 'description')), array(array('data'=>'HtmlTag'),array('tag'=>'td')), array('Label',array('tag'=>'td')), array(array('row'=>'HtmlTag'),array('tag'=>'tr')) )); $this->setElementDecorators( array( 'ViewHelper', 'Errors', array('Description',array('tag' => 'p', 'class' => 'description')), array(array('data'=>'HtmlTag'),array('tag'=>'td')), array('Label',array('tag'=>'td','class'=>'hide')), array(array('row' => 'HtmlTag'), array('tag' => 'tr')) ), array('submit') ); $this->setElementDecorators( array( 'ViewHelper', 'Errors', array(array('data'=>'HtmlTag'),array('tag'=>'td', 'colspan'=>2)), array(array('row' => 'HtmlTag'), array('tag' => 'tr','class'=>'hide')), ), array('csrf') );
これでDLをTableに出来るのだけども…
何故かCatpchaのhidden要素がテキストエリアで複製される。
さらに、File要素を追加すると、『Warning: Exception caught by form: No file decorator found… unable to render file element 』というエラーが出る。
applications/forms/GuestBook.php
$this->addElement('file', 'file', array( 'label' => 'File:', 'required' => false, 'destination'=> APPLICATION_UPLOADS_DIR, 'validators' => array( array( 'validator' => 'Count', 'options' => 1 ), array( 'validator'=>'Extension', 'options'=>'jpg,gif,png' ) ) ));
この記事によると
つまりZend_Form_Element_Fileを利用する時はZend_Form_Decorator_Formが必須となるという事。
となるとViewHelperは使わないという事を意味する。
ということだったので、多分Fileと同様にCapchaも同じ仕様なんでしょう。
setElementDecoratorsを別に作るか、
$this->setElementDecorators(array( 'Errors', array('Description', array('tag' => 'p', 'class' => 'description')), array(array('data'=>'HtmlTag'),array('tag'=>'td')), array('Label',array('tag'=>'td')), array(array('row'=>'HtmlTag'),array('tag'=>'tr')) ),array('catpha', 'files'));
個別にsetDecoratorsを当ててViewHelperを外せば直る。
$file = $this->getElement('file'); $file->setDecorators(array( array('File', array()), array('HtmlTag', 'options' => array('tag' => 'td')), array('Label', array('tag' => 'td')), array(array('row' => 'HtmlTag'), array('tag' => 'tr')) )); $captcha = $this->getElement('captcha'); $captcha->setDecorators(array( array('HtmlTag', 'options' => array('tag' => 'td')), array('Label', array('tag' => 'td')), array(array('row' => 'HtmlTag'), array('tag' => 'tr')) ));
要素にはgetElementの代わりに$this->fileでもアクセスできる。
なんでこんな二度手間な仕様なのかいまいち理解できないわ 😐
DisplayGroup
フォームってリセットボタンやキャンセルボタンもあるじゃないですか。
で、何も考えずにリセットボタンを入れると送信フォームの下に出ます。
リセットボタンを送信フォームの横に並べるならaddDisplayGroupです。
addDisplayGroupはFieldsetでグループ分けするものだけど、タグを指定すれば別の要素で分けることが出来るものです。
$this->setElementDecorators( array( 'ViewHelper', 'Errors', array('decorator' => 'Description', 'options' => array('tag' => 'p', 'class' => 'description')) ), array('submit','reset') ); $this->addDisplayGroup( array('submit', 'reset'), 'buttons', array('decorators' => array('FormElements', array('HtmlTag',array('tag'=>'td', 'colspan'=>2, 'class'=>'buttons')), array(array('row' => 'HtmlTag'), array('tag' => 'tr')), ) ) );
addDisplayGroupのラベルはキーがLegendです。fieldsetなだけに。
$this->addDisplayGroup( array('submit', 'reset'), 'buttons', array('decorators' => array('FormElements', array('HtmlTag',array('tag'=>'td', 'class'=>'buttons')), array(array('regend' => 'HtmlTag'), array('tag' => 'td')), ) ) );
ZeroMailでもそうなんだけど、私はボタンを入力項目とは別にコーディングするクセがあります。
そこでZend_Formでもボタンをtableから外してdiv要素に入れたい。と思ったとき、
decoratorsのHtmlTagをDivにすればいいんだけど、
applications/forms/GuestBook.php
$this->addDisplayGroup( array('submit', 'reset'), 'buttons', array('decorators' => array('FormElements', array('HtmlTag',array('tag'=>'div', 'class'=>'buttons')), ) ) );
※まとめて書くのをやめるとこうなります。
$buttons = $this->getDisplayGroup('buttons'); $buttons->setDecorators(array( 'FormElements', array('HtmlTag',array('tag'=>'div','class'=>'buttons')) ));
レンダリングされると何故か他のフォーム要素を無視して一番上に来ている。
ソースの順番を入れ替えても全く変化ナシ。
聞きかじったところによると、どうもsetDecoratorsが原因らしい。
SubForm
結局のところZend_Form_SubFormを使ったら思惑通りの表示になりました。
applications/forms/GuestBook.php
$this->setMethod('post'); $main = new Zend_Form_SubForm(); $main->addElement('text', 'email', array( 'label' => 'Your email address:', 'required' => true, 'filters' => array('StringTrim'), 'validators' => array( 'EmailAddress', ) )); $main->addElement('textarea', 'comment', array( 'label' => 'Please Comment:', 'rows' => 5, 'required' => true, 'validators' => array( array('validator' => 'StringLength', 'options' => array(0, 20)) ) )); $main->addElement('captcha', 'captcha', array( 'label' => 'Please enter the 5 letters displayed below:', 'required' => true, 'captcha' => array( 'captcha' => 'Figlet', 'wordLen' => 5, 'timeout' => 300 ) )); $this->addSubForm($main, 'main'); $this->getSubForm('main') ->clearDecorators() ->addDecorator('FormElements') ->addDecorator('HtmlTag',array('tag'=>'table', 'class' => 'zend_form')); $main->setElementDecorators( array( 'ViewHelper', 'Errors', array('decorator' => 'Description', 'options' => array('tag' => 'p', 'class' => 'description')), array('decorator' => 'HtmlTag', 'options' => array('tag' => 'td')), array('decorator' => 'Label', 'options' => array('tag' => 'th')), array('decorator' => array('OuterHtmlTag' => 'HtmlTag'), 'options' => array('tag' => 'tr')), ) ); $main->captcha->setDecorators(array( array('HtmlTag', 'options' => array('tag' => 'td')), array('Label', array('tag' => 'td')), array(array('row' => 'HtmlTag'), array('tag' => 'tr')) )); $sub = new Zend_Form_SubForm(); $sub->addElement('submit', 'submit', array( 'ignore' => true, 'label' => 'Sign Guestbook', )); $sub->addElement('reset', 'reset', array( 'ignore' => true, 'label' => 'Reset', )); $sub->addElement('hash', 'csrf', array( 'ignore' => true, )); $this->addSubForm($sub, 'subform'); $this->getSubForm('subform') ->clearDecorators() ->addDecorator('FormElements') ->addDecorator('HtmlTag',array('tag'=>'div', 'class' => 'buttons')); $sub->setElementDecorators(array('viewHelper','Errors'));
オプションでform要素を表示しないように設定しています。
Viewで直接ソースを書く
もうひとつの方法として、$this->form->elementName
でフォーム要素を個別に取得出来るので、テンプレートにデコレータのソースを直接書いてしまうという手もありました。
ファイルを2枚触る必要があるものの見た目からして分かりやすいし、こっちのほうがフリーダムにレイアウトできそうです。
views/scripts/guestbook/sign.phtml
<form action="<?php echo $this->url() ?>" method="post" enctype="multipart/form-data"> <table class="zend_form"> <tbody> <tr> <td id="email-label"><label class="required" for="email">Your email address:</label></td> <td><?php echo $this->form->email; ?></td> </tr> <tr> <td id="comment-label"><label class="required" for="comment">Please Comment:</label></td> <td><?php echo $this->form->comment; ?></td> </tr> <tr> <td id="captcha-input-label"><label class="required" for="captcha-input">Please enter the 5 letters displayed below:</label></td> <td><?php echo $this->form->captcha; ?></td> </tr> </tbody> </table> <div class="buttons"><?php echo $this->form->submit; ?> <?php echo $this->form->reset; ?> <?php echo $this->form->csrf; ?></div> </form>
applications/forms/GuestBook.php
$this->setMethod('post'); $this->setAttrib('enctype', 'multipart/form-data'); $this->addElement('text', 'email', array( 'label' => '', 'required' => true, 'filters' => array('StringTrim'), 'validators' => array( 'EmailAddress', ) )); $this->addElement('textarea', 'comment', array( 'label' => '', 'rows' => 5, 'required' => true, 'validators' => array( array('validator' => 'StringLength', 'options' => array(0, 20)) ) )); $this->addElement('captcha', 'captcha', array( 'label' => '', 'required' => true, 'captcha' => array( 'captcha' => 'Figlet', 'wordLen' => 5, 'timeout' => 300 ) )); $this->addElement('submit', 'submit', array( 'ignore' => true, 'label' => 'Sign Guestbook', )); $this->addElement('reset', 'reset', array( 'ignore' => true, 'label' => 'Reset', )); $this->addElement('hash', 'csrf', array( 'ignore' => true, )); $this->setElementDecorators(array( 'viewHelper', 'Errors', array('Description', array('tag' => 'p', 'class' => 'description')), )); $captcha = $this->getElement('captcha'); $captcha->setDecorators(array());
「[ZF] Zend_Form 要素に対するデコレータの設定についての色々」への3件のフィードバック