如何创建一个自定义的验证约束
你可以继承基础的约束类(Constraint)来创建一个自定义的约束。例如,你要去创建一个简单的验证器,检查一个字符串是否是只包含字母和数字的字符。
创建约束类 ¶
首先,你需要创建一个约束类并继承Constraint:
1
2
3
4
5
6
7
8
9
10
11
12
|
// src/AppBundle/Validator/Constraints/ContainsAlphanumeric.PHP
namespace AppBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
*/
class ContainsAlphanumeric extends Constraint
{
public $message = 'The string "%string%" contains an illegal character: it can only contain letters or numbers.';
} |
为了让这个新的约束,在类中能够以“注释”的方式来使用,你要加入@Annotation
注释。你约束的配置选项,将以公有属性的方式,在约束类中呈现。
创建验证器本身 ¶
正如您看到的一样,一个验证类是十分简洁的。他真正的验证是被其他“constraint validator(约束验证器)”类执行。这个约束验证器类被约束的validatedBy()
方法所指定,他包含一些简单的默认逻辑:
|
// in the base Symfony\Component\Validator\Constraint class
public function validatedBy()
{
return get_class($this).'Validator';
} |
换句话说,如果你创建了一个自定义Constraint
(例如 MyConstraint
),当实际执行验证时,symfony将自动寻找MyConstraintValidator
类:
这个验证类也很简单,并且只有一个validate()
方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// src/AppBundle/Validator/Constraints/ContainsAlphanumericValidator.php
namespace AppBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class ContainsAlphanumericValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if (!preg_match('/^[a-zA-Z0-9]+$/', $value, $matches)) {
$this->context->buildViolation($constraint->message)
->setParameter('%string%', $value)
->addViolation();
}
}
} |
在validate
里,我们不需要去返回一个值。相反,如果他的内容是非法的,你需要添加非法操作到验证器的context属性,如果他的内容是合法的$value
值将被认为是有效的。buildViolation
方法会把错误信息作为参数并返回一个ConstraintViolationBuilderInterface实例。 addViolation
方法最后会把不合法的内容添加到context。
使用这个新的验证器 ¶
使用自定义验证器是很容易的,就和使用 Symfony 本身提供方式一样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// src/AppBundle/Entity/AcmeEntity.php
use Symfony\Component\Validator\Constraints as Assert;
use AppBundle\Validator\Constraints as AcmeAssert;
class AcmeEntity
{
// ...
/**
* @Assert\NotBlank
* @AcmeAssert\ContainsAlphanumeric
*/
protected $name;
// ...
} |
|
# src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\AcmeEntity:
properties:
name:
- NotBlank: ~
- AppBundle\Validator\Constraints\ContainsAlphanumeric: ~ |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<!-- src/AppBundle/Resources/config/validation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="Http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
<class name="AppBundle\Entity\AcmeEntity">
<property name="name">
<constraint name="NotBlank" />
<constraint name="AppBundle\Validator\Constraints\ContainsAlphanumeric" />
</property>
</class>
</constraint-mapping> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// src/AppBundle/Entity/AcmeEntity.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use AppBundle\Validator\Constraints\ContainsAlphanumeric;
class AcmeEntity
{
public $name;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('name', new NotBlank());
$metadata->addPropertyConstraint('name', new ContainsAlphanumeric());
}
} |
如果在您定义的约束类中含有可选择的属性时,您应该在创建自定义约束类的时候就以公有的方式声明这些属性,那么这些可选择的属性就可以像使用 核心 Symfony 约束类中的属性一样使用它们。
带有依赖关系的约束验证器 ¶
如果你的约束验证器有依赖关系,比如一个数据库连接,你要把他配置为一个依赖注入容器的服务。这个服务一定要包含validator.constraint_validator
标签以便让验证系统知道它:
|
# app/config/services.yml
services:
validator.contains_alphanumeric:
class: AppBundle\Validator\Constraints\ContainsAlphanumericValidator
tags:
- { name: validator.constraint_validator } |
|
<!-- app/config/services.xml -->
<service id="validator.contains_alphanumeric" class="AppBundle\Validator\Constraints\ContainsAlphanumericValidator">
<argument type="service" id="doctrine.orm.default_entity_manager" />
<tag name="validator.constraint_validator" />
</service> |
|
// app/config/services.php
$container
->register('validator.contains_alphanumeric', 'AppBundle\Validator\Constraints\ContainsAlphanumericValidator')
->addTag('validator.constraint_validator'); |
现在,当symfony找到ContainsAlphanumericValidator
验证器,就会从容器加载此服务。
在Symfony的早期版本中,标签(tag)必须有一个alias
键(通常设置这个类的名称)。它仍然允许你的约束validateBy
方法返回别名(而不是一个类名)。
如:
|
public function validatedBy()
{
return 'alias_name';
} |
类的约束验证 ¶
除了验证一个类的属性,一个约束也能验证一个类作用域,是通过在他的Constraint
类中提供一个目标
|
public function getTargets()
{
return self::CLASS_CONSTRAINT;
} |
验证类中的 validate()
方法把这个对象作为它的第一个参数:
|
class ProtocolClassValidator extends ConstraintValidator
{
public function validate($protocol, Constraint $constraint)
{
if ($protocol->getFoo() != $protocol->getBar()) {
$this->context->buildViolation($constraint->message)
->atPath('foo')
->addViolation();
}
}
} |
注意,一个类约束验证器应用于类本身,而不是属性:
|
/**
* @AcmeAssert\ContainsAlphanumeric
*/
class AcmeEntity
{
// ...
} |
|
# src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\AcmeEntity:
constraints:
- AppBundle\Validator\Constraints\ContainsAlphanumeric: ~ |
|
<!-- src/AppBundle/Resources/config/validation.xml -->
<class name="AppBundle\Entity\AcmeEntity">
<constraint name="AppBundle\Validator\Constraints\ContainsAlphanumeric" />
</class> |