Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

SymfonyのForm:自分のやり方(My Practice)

More than 1 year has passed since last update.

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::classEmailType::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行だけで美しいフォームが出ます。

最後に

いつものように書きながら調べて、初めて気が付いたことが多いです。
アウトプット大事。

もっと言えば、ちゃんとマニュアル通りにコーディングすればいいだけ、という話だったりします。ただドキュメント量が多いのと、あちこちに散らかっているので分かりずらいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away