8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SymfonyAdvent Calendar 2018

Day 8

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

Posted at

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

最後に

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

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

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?