1. yutachaos

    Posted

    yutachaos
Changes in title
+symfony カスタムバリデーションの作り方
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,422 @@
+# はじめに
+symfonyは非常に強力なformクラスを持ったフレームワークです。
+案件で使いはじめたのですが、まとまった情報が少なく
+(公式documentは豊富なんですが、最新は英語だけなのと必要な情報がどれかを調べるのが辛い)
+備忘録も兼ねて、機能のまとめを作成してます。
+前回、前々回でformの作成とvalidationの記述の仕方を書きました。
+
+今回はvalidation ruleを作成することができるカスタムバリデーションのことについてまとめたいと思います。
+## 環境
+- symfony 2.8
+- php 7.0
+### 今まで書いたsymfonyの記事
+- symfony でのform作成の基本
+ - http://qiita.com/yutaChaos/items/0ae0d1797db4cb16466c
+- symfony validation処理の方法
+ - http://qiita.com/yutaChaos/items/95e70ef96608809bf475
+
+## カスタムバリデーションの作り方
+フォームや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
+- http://symfony.com/doc/2.8/validation/custom_constraint.html