Symfony Advent Calendar 2018の8日目が空いていたので、参加することにしました。SymfonyのFormコンポーネントでの自分の使い方です。
DTOを定義する。
データのエンティティは直接使わないで、DTOクラスを定義して使ってます。まず、ユーザー用DTOクラス、UserDTO
と、そこに「埋め込まれた(Embed)」された住所用DTOクラス、AddressDTO
を定義。
use Symfony\Component\Validator\Constraints as Assert;
class UserDTO
{
/**
* @var string
* @Assert\NotBlank()
*/
public $email;
/**
* @var AddressDTO
* @Assert\Valid() // ←(1)Validをつけること!
*/
public $address;
}
class AddressDTO
{
/**
* @var string
* @Assert\NotBlank()
*/
public $post_code;
/**
* @var string
* @Assert\NotBlank()
*/
pubic $address;
}
直接エンティティにバリデーションのアノテーションを使うこともできますが、個人的にDTOを別途用意するのが好きです。
DTOなので、プロパティはpublicにしてます。
ちなみにDTOを使う理由ですが…
- エラーがあった時でも不正な状態のエンティティを作らないで済みます。
- ValidationはDTOを見ればいいので、すぐわかります。
- エンティティからセッターを外せます。
- データのバリデーションは、エンティティ内かDBで行ってます。
バリデーションのConstraints
@Assert\NotBlank()
はSymfony/Validationの機能でConstraintsと呼ばれてます。
Assert\Valid()タグ
別フォームを埋め込むときは、埋め込むクラス(ここではUserDTO
)にAssert\Valid()
アノテーションを書いておくこと。
これがないと、埋め込まれたクラスのバリデーションが効きません。
FormTypeを定義する
AbstractType
を継承して、フォームを定義します。
use Symfony\Component\Form\AbstractType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, [
'label' => '確認先名',
])
->add('email', MailType::class, [
'label' => 'メールアドレス',
])
->add('address', AddressType::class, [
'error_bubbling' => false,
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => UserDTO::class,
));
}
}
class AddressType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('post_code', TextType::class, [
'label' => '郵便番号',
])
->add('address', TextType::class, [
'label' => '住所',
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => AddressDTO::class,
));
}
}
TextType::class
やEmailType::class
はビルドインフィールドタイプと呼んでいて、いろいろなオプションを選べます。
configureOptionsメソッド
configureOptions
で利用するDTOあるいはエンティティを指定します。指定がなくても動いてしまいますが、これがないとエラーがバブルしてしまいます。
また、指定がないと埋め込んだフォームのDTOに対して配列でアクセスしようとするようです。
要は「指定しとけ」っていうことです。
Controller/Twigで利用する
ここまで書いておけば、Controllerで簡単にフォームを利用できます。
class MyController extends Controller
{
public function form()
{
$form = $this->createForm(UserType::class);
return $this->render('form.html.twig', [
'form' => $form->createView(),
]);
}
}
Twigテンプレート
{{ form_start(form) }}
{{ form_widget(form) }}
<input type="submit" class="btn" value="保存">
{{ form_end(form) }}
Bootstrapテーマなどを設定しておけば、上の4行だけで美しいフォームが出ます。
最後に
いつものように書きながら調べて、初めて気が付いたことが多いです。
アウトプット大事。
もっと言えば、ちゃんとマニュアル通りにコーディングすればいいだけ、という話だったりします。ただドキュメント量が多いのと、あちこちに散らかっているので分かりずらいです。