symfony validation処理の方法

# はじめに
案件でとりあえずsymfony使い始めたけど、情報がまばらで苦労してます。
自分の復習や備忘録も兼ねてのsymfony記事です。
今回はvalidationについてまとめようと思います。

一個目のformの記事
http://qiita.com/yutaChaos/items/0ae0d1797db4cb16466c

## 環境
- symgony 2.8 
- php 7.0

## validationについて
 - symfonyでのvalidationは大きく分けて2つあります。(僕が現時点で知っているやつですが)
   - entityに直接記述する
   - FormTypeに記述する

### entityに直接記述する方法
今回使用するentityの基本コードはこれです。

ToDo.php

```
namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * ToDo
 *
 * @ORM\Table(name="to_do")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\ToDoRepository")
 */
class ToDo
{

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="task", type="integer", length=1)
     */
    private $task;

    /**
     * @var string
     * @Assert\NotBlank()
     * @Assert\Length(min = 2,max = 255)
     * @ORM\Column(name="memo", type="string", length=255, nullable=true)
     */
    private $memo;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="r_datetime", type="datetime", nullable=true)
     */
    private $rDatetime;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="u_datetime", type="datetime", nullable=true)
     */
    private $uDatetime;

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set task
     *
     * @param string $task
     * @return ToDo
     */
    public function setTask($task)
    {
        $this->task = $task;

        return $this;
    }

    /**
     * Get task
     *
     * @return string 
     */
    public function getTask()
    {
        return $this->task;
    }

    /**
     * Set memo
     *
     * @param string $memo
     * @return ToDo
     */
    public function setMemo($memo)
    {
        $this->memo = $memo;

        return $this;
    }

    /**
     * Get memo
     *
     * @return string 
     */
    public function getMemo()
    {
        return $this->memo;
    }

    /**
     * Set rDatetime
     *
     * @param \DateTime $rDatetime
     * @return ToDo
     */
    public function setRDatetime($rDatetime)
    {
        $this->rDatetime = $rDatetime;

        return $this;
    }

    /**
     * Get rDatetime
     *
     * @return \DateTime
     */
    public function getRDatetime()
    {
        return $this->rDatetime;
    }

    /**
     * Set uDatetime
     *
     * @param \DateTime $uDatetime
     * @return ToDo
     */
    public function setUDatetime($uDatetime)
    {
        $this->uDatetime = $uDatetime;

        return $this;
    }

    /**
     * Get uDatetime
     *
     * @return \DateTime
     */
    public function getUDatetime()
    {
        return $this->uDatetime;
    }

    /**
     * @ORM\PrePersist
     */
    public function refreshRDatetime()
    {
        $this->setRDatetime(new \Datetime());
    }

    /**
     * @ORM\PrePersist
     * @ORM\PreUpdate
     */
    public function refreshUDatetime()
    {
        $this->setUDatetime(new \Datetime());
    }

}
```

- $memoにNotBlankとLengthの制約をかけるためにannotationを付与しています。
これだけです。めっちゃ簡単。optionを設定する場合は `@Assert\Length(min = 2,max = 50)`のように引数に値を付与すれば設定ができます。

```
    /**
     * @var string
     * @Assert\NotBlank()
     * @Assert\Length(min = 2,max = 255)
     * @ORM\Column(name="memo", type="string", length=255, nullable=true)
     */
    private $memo;

```

### FormTypeに記述する

次にFormTypeに記載する場合です。

ToDoType

```
<?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type as InputType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints as Assert;

class ToDoType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('task',
                InputType\ChoiceType::class,
                [
                    'choices' => [
                        '1' => 'work',
                        '2' => 'hobby'
                    ],

                    'constraints' =>
                        [
                            new Assert\NotBlank()
                        ]
                ]
            )
            ->add('memo',
                InputType\TextareaType::class,
                [
                    'constraints' => [
                        new Assert\NotBlank(),
                        new Assert\Length(['min' => 2, 'max' => 255])
                    ]
                ]);

        $entity = $builder->getData();

        if(!$entity->getId()){
            $builder->add('Create', InputType\SubmitType::class,
                [
                    'attr' => [
                        'class' => 'btn btn-primary'
                    ]
                ]
            );
        }else{
            $builder->add('Edit', InputType\SubmitType::class,
            [
            'attr' => [
                'class' => 'btn btn-primary'
            ]
                ]
            );
        }
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\ToDo'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_todo';
    }


}

```
ここではentityと同じvalidationルール(NotBlank,Length)を書いています。
`add`関数は第1引数がentityのメンバーの名前、第2引数はそのメンバーで利用したいフォームの種類、そして第3引数にはoptionが設定できます。
Entityと同様のvalidationはFormTypeでのvalidationは第三引数に配列キーをconstraintsで渡すことで設定できます。
ただEntityに記載する場合と異なるのが、FormTypeは実際に生成されるFormの情報を記載しているので、
制約だけではなく、入力フォームの種類も記載できます。
第二引数では`TextareaType`を渡していますが、この情報を持ったFormを実際に表示するとtestareaのフォームが表示されます。
それだけではなく、formの種類に寄ったvalidationも自動的に付与されます。
例えば `NumberType`をフォームの種類を設定した場合は数字以外を弾くvalidationが設定されます。
FormTypeで規定する場合は適切なFormの種類を設定した上でconstraintsを設定することを考えると幸せになれると思います。
(前にTextTypeにGreaterThanを設定したら、普通の文字列がvalidationで弾かれなくて汗かいた)

http://symfony.com/doc/current/reference/constraints/GreaterThan.html
```
            ->add('memo',
                InputType\TextareaType::class,
                [
                    'constraints' => [
                        new Assert\NotBlank(),
                        new Assert\Length(['min' => 2, 'max' => 255])
                    ]
                ])
```

# 終わりに
symfonyには強力なvalidation機能が備わっていて、標準のvalidationでも結構いろいろなことができます。
さらに自分でカスタムvalidationを作成して、付与することが出来ます。
entityでもvalidation書けるけど、やっぱりEntityはデータ構造や付随する情報ぐらいにとどめて、実際のvalidationはFormTypeに任せた方がいいかなーと現時点では思っています。
(Best Practice読んでもValidation周りはよくわからんかった。。。)

# 参考
http://symfony.com/doc/current/reference/constraints.html
http://symfony.com/doc/current/reference/forms/types.html
http://symfony.com/doc/current/form/events.html