如何使用Symfony组件配置自定义验证器的依赖项?

时间:2016-11-15 04:08:52

标签: php validation dependency-injection symfony

我在项目中使用 Symfony Validator Symfony Service Container ,但不是完整的Symfony Framework。

我编写了一个自定义验证器,它需要一些依赖项作为构造函数参数提供。

Symfony Documentation描述了将验证器配置为服务的方法,我已成功完成。但是,重要的是使用validator.constraint_validator 服务标签,我怀疑它们会粘在一起。

此服务标签似乎特定于使用完整的Symfony框架时 - 当我添加此标记时,Symfony Validator并没有神奇地加载我的自定义验证器(毫不奇怪。)

那么,我如何链接Validator和Service Container组件?或者是否有任何其他策略让Symfony Validator加载带有依赖项的客户验证器?

1 个答案:

答案 0 :(得分:1)

你是对的,这个标签(validator.constraint_validator)由编译器处理来自Symfony/FrameworkBundle。 如果您不想使用此捆绑包,但想要将DI容器与验证器链接,则应重新实施此过程。

首先,我们需要一个ConstraintValidatorFactoryInterface实例。 Default implementation只查找一个类,其名称由validatedBy()约束方法返回。 Implementation from FrameworkBundle将validatedBy()的结果视为服务标识符/或标记别名,并从容器中获取此服务。我们的实现应该做那样的事情:

use Symfony\Component\DependencyInjection\ContainerInterface;

use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
use Symfony\Component\Validator\Constraint;

class ContainerConstraintValidatorFactory implements ConstraintValidatorFactoryInterface
{
    private $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function getInstance(Constraint $constraint)
    {
        // Simplified implementation, just for example:
        return $this->container->get($constraint->validatedBy());
    }
}

例如,假设我们有一些自定义验证约束和验证器类具有一些依赖性:

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

class CustomConstraint extends Constraint
{
    public $message = 'Oops!';

    public function validatedBy()
    {
        return 'validator.custom';
    }
}

class CustomConstraintValidator extends ConstraintValidator
{
    private $dependency;

    public function __construct($dependency)
    {
        $this->dependency = $dependency;
    }

    public function validate($value, Constraint $constraint)
    {
        if (rand() % 2 === 0) {
            $this->context
                ->buildViolation($constraint->message)
                ->addViolation();
        }
    }
}

接下来,我们将约束验证器注册为具有与相关约束对象的validatedBy()方法的结果相同的ID的服务。我们通过这个工厂创建与容器相关的验证器服务的实例。所有约束验证器都以相同的方式注册。

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Validator\Validation;

$container = new ContainerBuilder();
$container->register(
    'validator.custom', 
    new CustomConstraintValidator('Some dependency value')
);

$validatorBuilder = Validation::createValidatorBuilder();
$validatorBuilder->setConstraintValidatorFactory(
    new ContainerConstraintValidatorFactory($container)
);
$validator = $validatorBuilder->getValidator();

$violations = $validator->validate('test value', [ 
    new CustomConstraint() 
]);

它有效,但我建议考虑使用FrameworkBundle来重新发明轮子。您可以将其配置为仅使用您需要的东西。