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

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

More than 3 years have passed since last update.

@yutachaos

symfony カスタムバリデーションの作り方

はじめに

symfonyは非常に強力なformクラスを持ったフレームワークです。
案件で使いはじめたのですが、まとまった情報が少なく
(公式documentは豊富なんですが、最新は英語だけなのと必要な情報がどれかを調べるのが辛い)
備忘録も兼ねて、機能のまとめを作成してます。

前回、前々回でformの作成とvalidationの記述の仕方を書きました。

今回はvalidation ruleを作成することができるカスタムバリデーションのことについてまとめたいと思います。

環境

  • symfony 2.8
  • php 7.0

今まで書いたsymfonyの記事

カスタムバリデーションの作り方

フォームやEntityは実装済みという前提で、日付のvalidationを作成していきます。

3ステップです。便利!

1. Constraintクラスの作成

  • Symfony\Component\Validator\Constraintクラスを継承した制約クラスの作成
<?php

namespace AppBundle\Validator\Constraint;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class CheckDateFormat extends Constraint
{
    public $message = 'Invalid date format';

    public function __construct($message= "")
    {
        if(!empty($message['message'])){
            $this->message = $message['message'];
        }
    }
}

2. ConstraintValidatorクラスの作成

  • Symfony\Component\Validator\ConstraintValidatorクラスを継承したvalidatorクラスの作成
<?php

namespace AppBundle\Validator\Constraint;

use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class CheckDateFormatValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
        if(self::checkDatetimeFormat($value)){
            $this->context
                ->buildViolation($constraint->message)
                ->addViolation();
        }
    }

    public function checkDatetimeFormat($datetime)
    {
        if (!self::validateDate($datetime)) {
            return true;
        }

        return false;
    }

    private function validateDate($date, $format = 'Y-m-d')
    {
        try {
            \DateTime::createFromFormat($format, $date);
            $info = \DateTime::getLastErrors();
        }catch (Exception $e){
            return false;
        }
        return !$info['errors'] && !$info['warnings'];
    }
}

3. Validationの設定

  • entityにannotionで付与する場合
    • 作成したConstraintのクラスをannotionで付与します。 例
<?php

namespace AppBundle\Entity;

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


/**
 * 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 string
     * @Custom\CheckDateFormat()
     * @ORM\Column(name="date", type="datetime",nullable=false)
     */
    private $date;


    /**
     * @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 date
     */
    public function getMemo()
    {
        return $this->memo;
    }


    /**
     * @return date
     */
    public function getDate()
    {
        return $this->date;
        return $this;
    }

    /**
     * @param string $date
     */
    public function setDate($date)
    {
        $this->date = $date;
    }
    /**
     * 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());
    }

}

作成したConstraintのnamespaceに別名をつけて

use AppBundle\Validator\Constraint as Custom;

validationを付与したいメンバにannotionを付与しています。

    /**
     * @var string
     * @Custom\CheckDateFormat()
     * @ORM\Column(name="date", type="datetime",nullable=false)
     */
    private $date;
  • FormTypeに付与する場合
    • 付与したいメンバーのOptionに設定したconstraintsに設定します。自分の設定したクラスを指定する以外は通常のvalidationをつける方法と変わりません。

<?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;
use AppBundle\Validator\Constraint as Custom;


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])
                    ]
                ])
            ->add('date',
                InputType\TextType::class,
                [
                    'constraints' => [
                        new Custom\CheckDateFormat(),
                    ]
                ]);

        $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';
    }


}

終わりに

  • 通常のvalidationでもある程度のアプリケーションを作成することができますが、実際に利用していく中で特殊なvalidationが必要になることがままあると思います。
    symfonyは疎結合が強く意識されたフレームワークでvalidationに関してもsymfonyの思想に合わせて作っていくと、個々のvalidationが切り離され、テストやvalidationをテストしやすいコードになると思います。

参考URL

10
Help us understand the problem. What is going on with this article?
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
10
Help us understand the problem. What is going on with this article?