基于其他约束的 Symfony 自定义验证器

Posted

技术标签:

【中文标题】基于其他约束的 Symfony 自定义验证器【英文标题】:Syfmony custom validator based on other Constraints 【发布时间】:2020-10-16 03:31:53 【问题描述】:

如何编写自定义验证器?

例如,我有这个工作代码:


use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\Validation;



        $input = [
            null,   //fail
            0,      //fail
            1,      //fail
            2,      //fail
            "12",   //ok - string can be (at least 2 chars)
            20,     //ok
            50      //ok
        ];

        $constraint = new Assert\All([
            // the keys correspond to the keys in the input array
            new Assert\NotBlank(),
            new Assert\AtLeastOneOf([
                new Assert\Sequentially([
                    new Assert\Type(['type' => 'int']),
                    new Assert\GreaterThanOrEqual(20)
                ]),
                new Assert\Sequentially([
                    new Assert\Type(['type' => 'string']),
                    new Assert\Length(2)
                ])
            ])
        ]);

        $validator = Validation::createValidator();

        $violations = $validator->validate($input, $constraint);

我想将“检查”打包到一个类中,例如:


        $input = [
            null,   //fail
            0,      //fail
            1,      //fail
            2,      //fail
            "12",   //ok - string can be (at least 2 chars)
            20,     //ok
            50      //ok
        ];

        $constraint = new Assert\All(
            new IdConstraint()
        );

        $validator = Validation::createValidator();

        $violations = $validator->validate($input, $constraint);


IdContrains 或 IdValidator 类应该是什么样子?这是我到目前为止得到的:

namespace App\Constraint;

use Symfony\Component\Validator\Constraint;

class IdConstraint extends Constraint

    public $message = 'The input " string " contains invalid values.';

namespace App\Constraint;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class IdValidator extends ConstraintValidator

    public function validate($value, Constraint $constraint)
    
        //what to put here???
    

提前致谢!

【问题讨论】:

输入你的逻辑并在逻辑被破坏时添加违规。还有 rtfm symfony.com/doc/current/validation/custom_constraint.html 我已经阅读了调频,但我不知道该把逻辑放在哪里?我已经发布了工作逻辑,但不确定如何将其集成到 IdValidator 类中。这就是我寻求帮助的原因。 逻辑在validate 方法中。用$value 做任何你想做的事。如果出现问题 - 向 this->context 添加/构建违规。 【参考方案1】:

我找到了以下解决方案:


namespace App\Constraint;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class GroupValidator extends ConstraintValidator

    public function validate($value, Constraint $constraint)
    
        $context = $this->context;

        $validator = $context->getValidator();
        $validations = $validator->validate($value, $constraint->getConstraints());

        if ($validations->count() > 0) 
            $this->context->buildViolation($constraint->message)
                ->setParameter(' value ', (string)$value)
                ->addViolation();
        
    

<?php

namespace App\Constraint;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @Annotation
 */
class IdConstraint extends Constraint

    public $message = 'The input " value " contains invalid values.';

    public function validatedBy()
    
        return GroupValidator::class;
    

    public function getConstraints()
    
        return [
            new Assert\NotBlank(),
            new Assert\AtLeastOneOf([
                new Assert\Sequentially([
                    new Assert\Type(['type' => 'int']),
                    new Assert\GreaterThanOrEqual(20)
                ]),
                new Assert\Sequentially([
                    new Assert\Type(['type' => 'string']),
                    new Assert\Length(2)
                ])
            ])
        ];
    

在测试代码中:

        $input = [
            null,   //fail
            0,      //fail
            1,      //fail
            2,      //fail
            "12",   //ok - string can be (at leas 2 chars)
            20,     //ok
            50      //ok
        ];

        $constraint = new Assert\All(
            new IdConstraint()
        );

作为替代方案,我看到在 Symfony 5.1 中,使用 compound 是可能的,但我无法在那里设置一个简单的错误消息。

<?php

namespace App\Constraint;

use Symfony\Component\Validator\Constraints\Compound;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @Annotation
 */
class IdConstraint1 extends Compound

    protected function getConstraints(array $options): array
    
        return [
            new Assert\NotBlank(),
            new Assert\AtLeastOneOf([
                new Assert\Sequentially([
                    new Assert\Type(['type' => 'int']),
                    new Assert\GreaterThanOrEqual(20)
                ]),
                new Assert\Sequentially([
                    new Assert\Type(['type' => 'string']),
                    new Assert\Length(2)
                ])
            ])
        ];
    

【讨论】:

【参考方案2】:

您没有指定您使用的 Symfony 版本,但如果您有 maker 包,创建验证器的最简单方法是 php bin/console make:validator

作为您的验证代码,您没有提供很多细节,但根据我从您的代码中了解到的情况,您的验证可能是这样的。

namespace App\Constraint;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class IdValidator extends ConstraintValidator

    public function validate($value, Constraint $constraint)
    
        $value = (int) $value;

        if ($value < 10) 
            $this->context->buildViolation($constraint->message)
                ->setParameter(' value ', $value)
                ->addViolation();
        
    

【讨论】:

我想使用其他约束来验证我的 Id 值。逻辑在问题中,也许我描述得不够好。我已经发布了我现在创建的解决方案的答案,并且似乎运行良好。当然欢迎改进:) 是的,我在你自己的解决方案中看到了,我没有理解正确

以上是关于基于其他约束的 Symfony 自定义验证器的主要内容,如果未能解决你的问题,请参考以下文章

Symfony2验证器约束大于其他属性

对自定义 symfony 约束进行单元测试

Symfony 验证 - 在自定义验证行为中设置属性路径

基于 Symfony2 中其他字段值的字段条件验证

在 Symfony2 中使用自定义身份验证提供程序

Grails - 从自定义验证器闭包调用内置约束