nazolabo

フリーランスのWebエンジニアが近況や思ったことを発信しています。

Symfony2のフォーム(Form)の使い方

Symfony2のフォームは、かなり使いやすくなっています。
1はModelとの依存が激しくて、凝ったことをしようとすると意味不明になりましたが、2は完全に切り離されており、かつ依存させることも可能になっています。

パッと使う


action側

$this->createFormBuilder()
        ->add('hoge', 'text')
        ->add('fuga', 'date')
        ->getForm();

if ($request->getMethod() == 'POST') {
  $form->bindRequest($request);
  if ($form->isValid()) {
    // ここに保存処理とかを書く
  }
}

return $this->render('HogeBundle:Fuga:index.html.twig', array('form' => $form->createView()));

template側

<form action="{{ path('post_page') }}" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form) }}
    <input type="submit" />
</form>

パッと使うだけなら、フォーム用のクラスを作らなくても、actionに内蔵されているcreateFormBuilderで、その場限りのフォームを作成することができます。テスト中は便利ですね。

パッと使う(Entity連動)


action側

$user = new User();
$this->createFormBuilder($user)
        ->add('name', 'text')
        ->add('registered', 'date')
        ->getForm();
if ($request->getMethod() == 'POST') {
  $form->bindRequest($request);
  if ($form->isValid()) {
    // ここに保存処理とかを書く
  }
}

bindした時点で、$userの、フォームのフィールド名に対応するメンバに、フォームの値が入っています。
簡単なフォームであれば、このままsave()するだけです。

ちゃんとクラスを作る


このままでもいいのですが、クラスを作ったほうが使い回しができます。同じフォームが複数箇所で出てくる時はもちろん、本開発ではフォームクラスに機能を入れたい場合もあるので、クラスを作ったほうがいいです。
クラスは一般的には、BundleのForm/Type以下に作るようです。Typeという名の通り、正確にはこれはフォームクラスではありません。フォームの種類を定義したクラスです。

<?php
// src/Hoge/FugaBundle/Form/Type/UserType.php

namespace Hoge\FugaBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class UserType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('name', 'text');
        $builder->add('registered', 'date');
    }

    public function getName()
    {
        return 'user';
    }
}

buildFormの、$builderが、createFormBuilderで返されるものと同じです。(FormBuilderクラス)

フォームの種類


addの第一引数がフィールド名、第二引数が型ですが、この型の種類は、
text, textarea, email, integer, money, number, password, percent, search, url, choice, entity, country, language, locale, timezone, date, datetime, time, birthday, checkbox, file, radio, collection, repeated, hidden, csrf, field, form
があります。

テンプレート側が全部まとめて出ちゃうんだけど、これだとデザインと合わせられない!個別に出したい!

{{ form_errors(form) }}

{{ form_row(form.name) }}
{{ form_row(form.registered) }}

{{ form_rest(form) }}

これで分離できます。errorsはエラー、rowは各フィールド、restはhiddenのもの(トークンなど)が出ます。
rowはラベルとかもくっついて出てくるので、それも分離したい場合は、

{{ form_label(form.name) }}
{{ form_errors(form.name) }}
{{ form_widget(form.name) }}

で、さらに分離できます。

分離できるのはわかった!でも入力フォームのサイズとかはどうやって変えるの?


CSSでやれ

あ、class指定したいなら

$this->add('name', 'text', array('attr' => array('class' => 'nantoka')));

で指定できますよ。他のHTMLオプションも指定できますよ。

えっそこで何か指定できるの?他に何あるの?


しらん。ここ見て。(多すぎる)

多いのはわかるけど、よく使うのくらい教えてよ!







attrHTMLのオプションをarrayで指定
required必須指定
max_length最大文字数
labelラベル
read_only読み込み専用

デフォルト値はどこで指定するの?


Entityに値が入っていれば、勝手にそれになるよ。
手動で入れたければ、
$form->get('name')->setData('デフォルトの文字列');
という感じ。

ところで複数選択のやつとかどうやって指定するの?

$builder->add('blood_type', 'choice', array(
  'choices' => array(
    1 => 'A',
    2 => 'B',
    3 => 'O',
    4 => 'AB',
  ), 'empty_value' => '選択してね!'));

こんな感じ。empty_valueを省略すると、全部選択肢になる。

ファイルは?


'file'で指定すると、UploadedFileクラスが入るので、あとはご自由に

バリデーションは?


(続く)(かもしれない)