支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
你可以继承基础的约束类(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()
方法所指定,他包含一些简单的默认逻辑:
1 2 3 4 5 | // 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;
// ...
} |
1 2 3 4 5 6 | # 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
标签以便让验证系统知道它:
1 2 3 4 5 6 | # app/config/services.yml
services:
validator.contains_alphanumeric:
class: AppBundle\Validator\Constraints\ContainsAlphanumericValidator
tags:
- { name: validator.constraint_validator } |
1 2 3 4 5 | <!-- 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> |
1 2 3 4 | // app/config/services.php
$container
->register('validator.contains_alphanumeric', 'AppBundle\Validator\Constraints\ContainsAlphanumericValidator')
->addTag('validator.constraint_validator'); |
现在,当symfony找到ContainsAlphanumericValidator
验证器,就会从容器加载此服务。
在Symfony的早期版本中,标签(tag)必须有一个alias
键(通常设置这个类的名称)。它仍然允许你的约束validateBy
方法返回别名(而不是一个类名)。
如:
1 2 3 4 | public function validatedBy()
{
return 'alias_name';
} |
除了验证一个类的属性,一个约束也能验证一个类作用域,是通过在他的Constraint
类中提供一个目标
1 2 3 4 | public function getTargets()
{
return self::CLASS_CONSTRAINT;
} |
验证类中的 validate()
方法把这个对象作为它的第一个参数:
1 2 3 4 5 6 7 8 9 10 11 | class ProtocolClassValidator extends ConstraintValidator
{
public function validate($protocol, Constraint $constraint)
{
if ($protocol->getFoo() != $protocol->getBar()) {
$this->context->buildViolation($constraint->message)
->atPath('foo')
->addViolation();
}
}
} |
注意,一个类约束验证器应用于类本身,而不是属性:
1 2 3 4 5 6 7 | /**
* @AcmeAssert\ContainsAlphanumeric
*/
class AcmeEntity
{
// ...
} |
1 2 3 4 | # src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\AcmeEntity:
constraints:
- AppBundle\Validator\Constraints\ContainsAlphanumeric: ~ |
1 2 3 4 | <!-- src/AppBundle/Resources/config/validation.xml -->
<class name="AppBundle\Entity\AcmeEntity">
<constraint name="AppBundle\Validator\Constraints\ContainsAlphanumeric" />
</class> |
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。