验证(Validation)
验证(Validation )是在Web程序中极为常见的任务。表单中输入的数据需要验证。写入到数据库或传送到Web服务时,数据也需要验证。
Symfony自带了一个 Validator 组件,它让校验工作变得简单和透明。该组件基于 JSR303 Bean校验规范。
验证的基础 ¶
理解验证的最好方法就是看它的实际应用。在开始之前,假设你创建了一个原生php对象,它用在程序需要的某个地方:
// src/AppBundle/Entity/Author.phpnamespace AppBundle\Entity; class Author{ public $name;}
目前为止,这只是一个为你的程序提供某些用途的普通类。验证的目的就是要告诉你,对象中的数据是否有效。为此,你需要配置一个规则列表(称为 constraints/约束 ),对象必须遵循之方能有效。这些规则可以通过多种不同的格式(YAML、XML、annotations或PHP)来指定。
比如,要保证属性 $name
不为空,添加以下内容:
PHP:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints\NotBlank; class Author{ public $name; public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('name', new NotBlank()); }}
XML:<!-- 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\Author"> <property name="name"> <constraint name="NotBlank" /> </property> </class></constraint-mapping>
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Author: properties: name: - NotBlank: ~
Annotations:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Constraints as Assert; class Author{ /** * @Assert\NotBlank() */ public $name;}
Protected和private属性以及“getter”方法也可以被验证(见 约束的投放范围)。
使用验证服务 ¶
接下来,要真正的校验 Author
对象,使用 validator
服务(Validator
类)的 validate
方法。 validator
的工作很简单:读取一个类的约束规则来校验对象数据是否满足这些约束。如果验证失败,一个非空的错误列表(ConstraintViolationList
类)将被返回。在控制器中实践这个简单例子:
// ...use Symfony\Component\HttpFoundation\Response;use AppBundle\Entity\Author; // ...public function authorAction(){ $author = new Author(); // ... do something to the $author object // ... 对 $author 对象做一些事 $validator = $this->get('validator'); $errors = $validator->validate($author); if (count($errors) > 0) { /* * Uses a __toString method on the $errors variable which is a * ConstraintViolationList object. This gives us a nice string * for debugging. * 对 $errors 变量,即 ConstraintViolationList 对象,使用 __toString 方法。 * 这给了我们一个美观的字符串用于调试。 */ $errorsString = (string) $errors; return new Response($errorsString); } return new Response('The author is valid! Yes!');}
如果 $name
属性是空的,你会看到一个错误信息:
1 2 | AppBundle\Author.name:
This value should not be blank |
如果你为 name
属性插入一个值,令人高兴的成功信息就会出现。
多数时候,你不需要直接跟 validator
服务互动,或者毋须为输出错误信息担心。多数情况下,你在处理提交过来的表单数据时,间接地使用validation验证。参考 验证与表单 以了解更多。
你也可以传递“错误信息集合”(collection of errors)到模版中:
if (count($errors) > 0) { return $this->render('author/validation.html.twig', array( 'errors' => $errors, ));}
在模版中,你可以根据需要精确输出错误列表:
PHP:<!-- app/Resources/views/author/validation.html.php --> <h3>The author has the following errors</h3> <ul><?php foreach ($errors as $error): ?> <li><?php echo $error->getMessage() ?></li><?php endforeach ?></ul>
Twig:{# app/Resources/views/author/validation.html.twig #}<h3>The author has the following errors</h3> <ul>{% for error in errors %} <li>{{ error.message }}</li>{% endfor %}</ul>
每一个验证错误(被称为“constraint violation/约束违反”)都由一个 ConstraintViolation
对象来呈现。
配置 ¶
Symfony的validator是默认开启的,但是如果你使用annotation方式来指定约束,必须显式地开启(用于验证的)annotation:
PHP:// app/config/config.php$container->loadFromExtension('framework', array( 'validation' => array( 'enable_annotations' => true, ),));
XML:<!-- app/config/config.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:framework="http://symfony.com/schema/dic/symfony" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> <framework:config> <framework:validation enable-annotations="true" /> </framework:config></container>
YAML:# app/config/config.ymlframework: validation: { enable_annotations: true }
约束规则 ¶
Validator
被设计成针对 约束(即规则)来验证对象。要验证一个对象,只需把一或多个约束映射到它要验证的类,然后再把它传递给 validator
服务即可。
在幕后,一个约束就是一个PHP对象,它可以生成一个用于决断的声明。在实践中,一个约束可以是“蛋糕不能烧”。在Symfony中,约束是类似的:它们就是“条件是否为真”的断言。给定一个值,约束会告诉你这个值是否遵守了你的约束规则。
支持的约束 ¶
Symfony封装了很多最常用的约束:
基本约束 ¶
这些是基本的约束:使用它们来断言属性值相关的非常基础的东西,或者断言你程序中的方法之返回值。
字符串约束 ¶
数字约束 ¶
比较约束 ¶
日期约束 ¶
Collection约束 ¶
文件约束 ¶
财务数字约束 ¶
其他约束 ¶
你也可以创建自己的自定义约束。如何创建自定义的验证约束 一文覆盖了此话题。
约束的配置 ¶
一些约束,像是 NotBlank 较为简单,而其他一些像是 Choice 约束,有许多可用的配置选项。假设 Author
类有另外一个属性叫 gender
,该属性可以被设置为“male”、“female” 或 “other”:
PHP:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints as Assert; class Author{ public $gender; // ... public static function loadValidatorMetadata(ClassMetadata $metadata) { // ... $metadata->addPropertyConstraint('gender', new Assert\Choice(array( 'choices' => array('male', 'female', 'other'), 'message' => 'Choose a valid gender.', ))); }}
XML:<!-- 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\Author"> <property name="gender"> <constraint name="Choice"> <option name="choices"> <value>male</value> <value>female</value> <value>other</value> </option> <option name="message">Choose a valid gender.</option> </constraint> </property> <!-- ... --> </class></constraint-mapping>
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Author: properties: gender: - Choice: { choices: [male, female, other], message: Choose a valid gender. } # ...
Annotations:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Constraints as Assert; class Author{ /** * @Assert\Choice( * choices = { "male", "female", "other" }, * message = "Choose a valid gender." * ) */ public $gender; // ...}
约束的选项始终可以通过一个数组来传递的。有些约束也允许你传入一个 ”default” 选项的值来代替这个数组。在 Choice
约束中,choices
选项就可以通过这种方式指定。
PHP:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints as Assert; class Author{ protected $gender; public static function loadValidatorMetadata(ClassMetadata $metadata) { // ... $metadata->addPropertyConstraint( 'gender', new Assert\Choice(array('male', 'female', 'other')) ); }}
XML:<!-- 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\Author"> <property name="gender"> <constraint name="Choice"> <value>male</value> <value>female</value> <value>other</value> </constraint> </property> <!-- ... --> </class></constraint-mapping>
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Author: properties: gender: - Choice: [male, female, other] # ...
Annotations:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Constraints as Assert; class Author{ /** * @Assert\Choice({"male", "female", "other"}) */ protected $gender; // ...}
这纯粹是为了让最常见的配置选项在用起来时更加的简单快速。
如果你不确定如何指定一个选项,要么去查看API文档,要么为了保险起见,通过一个选项数组来传入(即上面第一种方式)。
约束的目标 ¶
约束可以被应用到类的属性(如 name
)或者一个公共的getter方法(如 getFullName
)乃至整个类上。属性约束最常用也最简单,而Getter约束则允许你指定更加复杂的验证规则。最后,如果,类约束的使用场景是,你要将类作为整体进行验证。
属性约束 ¶
类属性的验证是一个最基本的验证技巧。Symfony允许你校验 private, protected 或者 public 属性。下面代码展示了如何配置 Author
对象的 $firstName
属性,令其至少有3个字符:
PHP:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints as Assert; class Author{ private $firstName; public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('firstName', new Assert\NotBlank()); $metadata->addPropertyConstraint( 'firstName', new Assert\Length(array("min" => 3)) ); }}
XML:<!-- 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\Author"> <property name="firstName"> <constraint name="NotBlank" /> <constraint name="Length"> <option name="min">3</option> </constraint> </property> </class></constraint-mapping>
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Author: properties: firstName: - NotBlank: ~ - Length: min: 3
Annotations:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Constraints as Assert; class Author{ /** * @Assert\NotBlank() * @Assert\Length(min=3) */ private $firstName;}
Getters约束 ¶
约束也可以应用于方法的返回值。Symfony允许你把一个约束添加到任何“get”、“is” 或者 “has”开头的public方法。这一类方法被称为“getters”。
这种技巧的好处是允许你动态验证你的对象。例如,假设你想确保密码字段不能匹配到用户的firstname(出于安全原因)。你可以通过创建一个 isPasswordLegal
方法,然后断言该方法必须返回 true
来实现:
PHP:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints as Assert; class Author{ public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addGetterConstraint('passwordLegal', new Assert\IsTrue(array( 'message' => 'The password cannot match your first name', ))); }}
XML:<!-- 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\Author"> <getter property="passwordLegal"> <constraint name="IsTrue"> <option name="message">The password cannot match your first name</option> </constraint> </getter> </class></constraint-mapping>
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Author: getters: passwordLegal: - 'IsTrue': { message: 'The password cannot match your first name' }
Annotations:// src/AppBundle/Entity/Author.php // ...use Symfony\Component\Validator\Constraints as Assert; class Author{ /** * @Assert\IsTrue(message = "The password cannot match your first name") */ public function isPasswordLegal() { // ... return true or false }}
现在,创建一个 isPasswordLegal()
方法,含有你所需的逻辑:
public function isPasswordLegal(){ return $this->firstName !== $this->password;}
眼尖的人可能会注意到,在YAML, XML和PHP的约束配置格式中,getter的前缀(“get”、”is” 或者 “has”) 在映射时被忽略了。这能让你在不改变验证逻辑的前提下,把一个约束移动到后面的一个同名属性之上(反之亦然)。
类约束 ¶
有一些约束可以应用到被验证的整个类。例如,Callback 类型的约束,就是一个可以作用到类本身的通用约束。当类被验证时,约束所指定的方法将被直接执行,以便提供更多的自定义验证。
总结 ¶
Symfony的 validator
(验证)是一个强大的工具,它可以被用来保证任何对象之数据的合法性。它的强大来源自约束规则(constraints),你可以把它应用到对象的属性和getter方法上。虽然,多数情况下都是在使用表单时间接应用了验证框架,记得,它可以用在任何地方去验证任何对象。