Symfony2 カスタムバリデータ

Symfony2標準の日付バリデータ
(vendor/symfony/symfony/src/Symfony/Component/Validator/Constraints/DateValidator.php)は、
以下の様に書式が「YYYY-MM-DD」形式となっています。

const PATTERN = '/^(\d{4})-(\d{2})-(\d{2})$/';

年月文字列「YYMM」や月日文字列「MMDD」(Month/day )が有効値か検証したいので、制約とバリデータを自作します。

Symfony2のバリデーションのしくみ

  • Symfony\Component\Validator\Constraint
public function validatedBy()
{
    return get_class($this).'Validator';
}

「制約クラス . Validator」という制約バリデータクラスを探し、そのバリデータの validate() を実行します。

制約クラスを用意する

  • Symfony\Component\Validator\Constraint を継承します。
  • 「@Annotation」というアノテーションを付けます。
  • public $message を定義して、エラーメッセージを設定します。
use Symfony\Component\Validator\Constraint;

/**
 * 月日文字列「MMDD」制約クラス
 * @Annotation
 *
 */
class DateMonthDay extends Constraint
{
    public $message = '有効な月日文字列「MMDD」ではありません';
}
use Symfony\Component\Validator\Constraint;

/**
 * 年月文字列「YYMM」制約クラス
 * @Annotation
 *
 */
class DateYearMonth extends Constraint
{
    public $message = '有効な年月文字列「YYMM」ではありません';
}

制約バリデータクラスを実装する

  • Symfony\Component\Validator\ConstraintValidator を継承します。
  • validate($value, Constraint $constraint) を実装します。
    • 戻り値はありません。
    • 無効値を検出した場合は、addViolation() を使って、エラーメッセージをコンテキストに追加します。
    • バリデータはエラーメッセージの有無でバリデーションエラーの有無を決定します。
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Constraint;

class DateYearMonthValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint) {
        if (!(is_string($value) or is_numeric($value))) {
            return;
        }

        $date = '20' . $value . '01';

        $dt = \DateTime::createFromFormat('Ymd', $date);

        $isValid = ($dt && $dt->format('Ymd') == $date);
        if (!$isValid) {
            $this->context->addViolation($constraint->message);
        }
    }
}
class DateMonthDayValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint) {
        if (!(is_string($value) or is_numeric($value))) {
            return;
        }

        $date = '2000' . $value;

        $dt = \DateTime::createFromFormat('Ymd', $date);

        $isValid = ($dt && $dt->format('Ymd') == $date);
        if (!$isValid) {
            $this->context->addViolation($constraint->message);
        }
    }
}

テストコード

class CustomConstraintValidatorTest extends WebTestCase
{
    protected $validator;

    public function setUp()
    {
        static::$kernel = static::createKernel();
        static::$kernel->boot();

        $this->validator = static::$kernel->getContainer()->get('validator');
    }

    public function testSuccess()
    {
        $errors = $this->validator->validate(new DateString('0229'));
        $this->assertCount(0, $errors, '');
    }

    public function testFailure()
    {
        $errors = $this->validator->validate(new DateString('0230'));
        foreach ($errors as $error) {
            echo $error->getPropertyPath() . " - " . $error->getMessage() . PHP_EOL;
        }
        $this->assertNotCount(0, $errors, '');
    }
}