sfFormのレンダリングにカスタムフォーマッタを使用する

symfonyのフォームライブラリsfFormを使ってフォームを表示する場合、フォームの各要素はデフォルトではテーブルの行として以下のようなHTMLタグで出力されます。

<tr>
  <th><label for="login_login_name">Login name</label></th>
  <td><input type="text" name="login[login_name]" id="login_login_name" /></td>
</tr>

フォーマッタは、デフォルトの「table」以外に「list」も用意されています。
(これらはそれぞれ、sfWidgetFormSchemaFormatterTableとsfWidgetFormSchemaFormatterListに対応します)
フォーマットをlistに変更するには、フォームの設定を行っている部分で以下のようにします。

<?php
class LoginForm extends sfForm {
  public function setup() {$this->widgetSchema->setDefaultFormFormatterName('list');
    :

こうすると、次のようなHTMLタグで出力されます。

<li>
  <label for="login_login_name">Login name</label>
  <input type="text" name="login[login_name]" id="login_login_name" />
</li>

出力されるタグのカスタマイズ

さて、出力されるタグを、独自クラスを指定したdivタグに変更する場合はどうするのでしょうか。
公式のドキュメント(Forms in Action)では、「テンプレートのカスタマイズ」のやり方しか書かれていません。

この方法ではテンプレートに直接フォーム要素を埋め込んでいくので、デザインに対しては柔軟になるのですが、フォーム要素を追加したり変更したりする場合にはテンプレートも合わせて修正しなくてはならないので、開発効率はかなり低下します。


フォーマッタの出力とテンプレートのカスタマイズの中間くらいのことをやりたいですよね。


そこで、sfWidgetFormSchemaFormatterListクラスを真似た、myWidgetFormSchemaFormatterDivクラスを次のように作成します。

<?php
class myWidgetFormSchemaFormatterDiv extends sfWidgetFormSchemaFormatter
{
  protected
    $rowFormat       = "<div class=\"formrow\">%error%%label%%field%%help%%hidden_fields%</div>\n",
    $errorRowFormat  = "<div class=\"error\">%errors%</div>\n",
    $helpFormat      = '<div class="help">%help%</div>',
    $decoratorFormat = "<div class=\"formcontent\">\n%content%\n</div>\n";
}

このフォーマッタを使用するために、formクラスのsetupメソッドで、次のようにフォーマッタを追加してデフォルトに設定します。

<?php
class LoginForm extends sfForm
{
  public function setup()
  {$this->widgetSchema->addFormFormatter('div', new myWidgetFormSchemaFormatterDiv($this->widgetSchema));
    $this->widgetSchema->setDefaultFormFormatterName('div');
    :


こうすると、myWidgetFormSchemaFormatterDivクラスのフォーマット設定でフォームがレンダリングされます。

<div class="formrow"><label for="login_login_name">Login name</label>
<input type="text" name="login[login_name]" id="login_login_name" /></div>

sfWidgetFormSchema::setFormFormatterNameを使用する場合の注意

上のサンプルコードではsetDefaultFormFormatterNameメソッドでフォーマッタを変更しましたが、同じような機能を持つsetFormFormatterNameメソッドがあります。
この2つの違いは、クラスのスタティック変数としてフォーマッタ名を保存するか、インスタンスのメンバ変数として保存するかです。1つの画面で複数のフォームオブジェクトを使用し、それぞれで別のフォーマッタを利用したい場合は、setFormFormatterNameを使用するということですね。