symfony : はじめてのsfForm

この記事はSymfonyアドベントカレンダー2010の22日目です

はじめに…の前に

symfonyに関わるようになってから半年くらいたちます。
思いっきり、仕事で関わってます。
実際使い始めて、「書きやすい」「扱いやすい」「自分に合ってる」というかんじでしたね。
自社開発ではほとんどがsymfony(1.4)での開発となっております。

他のみなさんの記事は、Symfony2だったり高度な記事が多くてちょっとびびってますが、敢えて初心に戻って(私が割と混乱してた)symfony1.4のフォームについて書いてみたいと思います。

はじめに

symfony 1.4になってから、フォームまわりがsfFormクラスからの生成のみとなり、input_tagとかいったものが使えなくなりました。
DRY的考え方だと「フォームもDon’t Repeat Yourself」で理にかなってるのですが、おそらくなじめない方も少なからずいるんじゃないかなと思います。
私のサイトのアクセスログを見ていると、「symfony 1.4 input_tag」での検索がぼちぼち来ていますので、この変更に混乱してるかたも少なからずいらっしゃるんだろうな、と思いまして、input_tagが使えない1.4以降のフォーム周りの基本的な話をしてみようかと思います。

考え方

フォームも1つのクラスとして考えます。
クラスとして定義することにより、使い回すことを想定しています。
module、template、form classの関係は下記の図のようになります。
2010122201

実際の扱い

(sfFormクラスの派生の)BaseFormをベースとしたformのクラスを生成します。
ファイルの場所は基本、 lib/form/ 以下となります。
このクラスで、フォームの部品やvalidationの定義します。
これらの定義は、configure()メソッドで行います。

簡単な例:まずは1つのテキストボックスで

1つのテキストボックスを持つフォームのクラスを定義する場合、下記のようになります。
ファイル名 SimpleForm.class.php です。

class SimpleForm extends BaseForm
{
  function configure()
  {
    $this->setWidgets(array(
        'intext' => new sfWidgetFormInputText() ,
    ));
  }
}

setWidgetsで、引数にフォームのウィジェットを定義した配列を与えてあげます。

これを実際のフォームとして表示するには、
・moduleのaction class内でこのformのインスタンスを作る
・templateで表示処理を書く

module(action.class.php)ではこうします。

function executeIndex(sfWebRequest $request)
{
  $this->form = new SimpleForm() ;
}

これに対応した、template(IndexSuccess.php)での表示処理はこうなります。

<form action="do.php" method="POST">
<table border="1">
<?php echo $form ; ?>
<tr><td colspan="2"><input type="submit" value="ぽちっとな"></td></tr>
</table>
</form>

$form だけの場合、レイアウトはデフォルトのままになります。
表示結果はこうなります。
2010122203

自身でレイアウトしたい場合、inputタグ該当部分は以下のように render() を使います。

<?php echo $form['intext']->render() ; ?>

スタイルを設定したい場合などは、render()の引数に配列で与えます。

<?php echo $form['intext']->render(array('style'=>'width:300px;')) ; ?>

フォーム系タグとウィジェットの対応

使いたいタグと、指定するウィジェットは下記のようになります。

input-text

<input type="text" ...>

$w = new sfWidgetFormInputText();

input-password

<input type="password" ...>

$w = new sfWidgetFormInputPassword();

input-hidden

<input type="hidden" ...>

$w = new sfWidgetFormInputHidden();

textarea

<textarea>
</textarea>

$w = new sfWidgetFormTextarea();

select(プルダウン)

<select>
 <option value="1">select 1</option>
 <option value="2">select 2</option>
 ...
</select>

static $choicelist = array('1'=>'select 1','2'=>'select 2',...);
$w = new sfWidgetFormSelect(array('choices'=>$choicelist));

radio(ラジオボタンセレクト)

<input type="radio" value="1">choice 1
<input type="radio" value="2">choice 2
...

static $choicelist = array('1'=>'select 1','2'=>'select 2',...);
$w = new sfWidgetFormChoice(array('choices'=>$choicelist));

主に使うのはこのへんでしょうか。

バリデーターを使ってみる

Formクラスのconfigure()メソッド内で、setValidatorでセットします。

class SimpleForm extends BaseForm
{
  function configure()
  {
    $this->setWidgets(array(
        'intext' => new sfWidgetFormInputText() ,
    ));
    $this->widgetSchema->setNameFormat('data[%s]');
    $this->setValidators(array(
                                'intext'=>new sfValidatorString(),
                         )) ;
  }
}

setValidatorの引数に連想配列でvalidatorクラスをセットします。

2つめのsetNameFormatは、名前のフォーマットを指定します。
この場合、「 data[intext] 」のような形になります。
バリデーション処理でバインドするとき(後述)にここの名前を使います。

話を元に戻して。
sfValidator〜 (validatorスキーマ)では、引数でvalidationの設定を行います。
以下の場合は、intextが必須でなくなります。

'intext'=>new sfValidatiorString(array('required'=>false)),

以下のようにすると、intextが必須になり、抜けていた場合は「intextは必須です。」のメッセージを返すようになります。

'intext'=>new sfValidatiorString(array('required'=>true),
                                 array('required'=>'intextは必須です。')),

validationにはねられた場合のエラー表示は、template内で下記のようにします。

<?php echo $form['intext']->renderError(); ?>

validatorのスキーマは文字列や数値、日付以外にも、Eメールなどいろんな種類があります。
多いのでここでは説明を割愛します。詳しくは下記にまとまってます、
symfony Forms in Action | 付録 B – バリデーター | symfony | Web PHP Framework

実際にバリデーションを行う、action.class.phpでは、以下のようにします。

    $form = new SimpleForm();
    $form->bind($request->getParameter('data'));

    $v = $form->isValid() ;
    if ( $v ) {
      // validation 成功
      $intext = $form->getValue('intext') ;
    }
    else
    {
      // validation 失敗の処理
    }

流れとしてはこうです。
1.フォームのクラスを作る
2.バインドする (form class内でsetNameFormatした名前を当てる)
3.バリデーションする

また、バリデーションを設定すると、デフォルトでCSRFチェックを行います。
自分でフォームをレンダリングする場合、template内で以下のようにCSRF対策用トークンを追加してあげてください(hiddenで渡されます)。

<?php echo $form['_csrf_token']->render(); ?>

どうしてもvalidationに失敗してしまう場合、多分これが抜けてる可能性があります。

最後に

フォームに関しては公式のsymfony Forms in Action | symfony | Web PHP Frameworkにいいまとめがあるのですが、少し読みづらいかんじがしました。
「とりあえず動くもの」を示してみて、そこから入っていけるためのとっかかりになってくれればなあ、と思いまして、記事を書いてみました。

参考

Symfony Advent 2010について

Symfony Advent 2010では12月1日から12月24日までを使って日替わりでsymfonyでイイなと思った小さなtipsから内部構造まで迫った解説などをブログ記事にして公開していくイベントです。

Comments are closed.