支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
验证(Validation )是在Web程序中极为常见的任务。表单中输入的数据需要验证。写入到数据库或传送到Web服务时,数据也需要验证。
Symfony自带了一个 Validator 组件,它让校验工作变得简单和透明。该组件基于 JSR303 Bean校验规范。
理解验证的最好方法就是看它的实际应用。在开始之前,假设你创建了一个原生php对象,它用在程序需要的某个地方:
1 2 3 4 5 6 7 | // src/AppBundle/Entity/Author.php
namespace AppBundle\Entity;
class Author
{
public $name;
} |
目前为止,这只是一个为你的程序提供某些用途的普通类。验证的目的就是要告诉你,对象中的数据是否有效。为此,你需要配置一个规则列表(称为 constraints/约束 ),对象必须遵循之方能有效。这些规则可以通过多种不同的格式(YAML、XML、annotations或PHP)来指定。
比如,要保证属性 $name
不为空,添加以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 | // src/AppBundle/Entity/Author.php
// ...
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank()
*/
public $name;
} |
1 2 3 4 5 | # src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\Author:
properties:
name:
- NotBlank: ~ |
1 2 3 4 5 6 7 8 9 10 11 12 | <!-- 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> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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());
}
} |
Protected和private属性以及“getter”方法也可以被验证(见 约束的投放范围)。
接下来,要真正的校验 Author
对象,使用 validator
服务(Validator
类)的 validate
方法。 validator
的工作很简单:读取一个类的约束规则来校验对象数据是否满足这些约束。如果验证失败,一个非空的错误列表(ConstraintViolationList
类)将被返回。在控制器中实践这个简单例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // ...
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)到模版中:
在模版中,你可以根据需要精确输出错误列表:
1 2 3 4 5 6 7 | <!-- 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> |
每一个验证错误(被称为“constraint violation/约束违反”)都由一个 ConstraintViolation
对象来呈现。
validator
服务可以在任何时候使用,来验证任何对象。 事实上,你将经常在处理表单时,间接使用 validator
。Symfony的表单类库间接使用 validator
服务来在数据被提交和绑定后验证底层对象。对象违反约束信息将被转化到 FormError
对象,该对象可以很容易的被展示在你的表单中。在一个控制器中看看的传统表单提交流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // ...
use AppBundle\Entity\Author;
use AppBundle\Form\AuthorType;
use Symfony\Component\HttpFoundation\Request;
// ...
public function updateAction(Request $request)
{
$author = new Author();
$form = $this->createForm(AuthorType::class, $author);
$form->handleRequest($request);
if ($form->isValid()) {
// the validation passed, do something with the $author object
return $this->redirectToRoute(...);
}
return $this->render('author/form.html.twig', array(
'form' => $form->createView(),
));
} |
该实例使用一个 AuthorType
表单类,更多信息请查看表单章。
Symfony的验证默认情况下是启用的。但是如果你使用了注释方法来指定你的约束,那么你需要显式的开启注释功能:
1 2 3 | # app/config/config.yml
framework:
validation: { enable_annotations: true } |
1 2 3 4 5 6 7 8 9 10 11 12 | <!-- 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> |
Validator
的设计目的是用来按照约束规则验证对象。为了验证一个对象,只需要映射一个或者多个约束到它要验证的类,然后把它传递给 validator
服务即可。
本质上,一个约束就是一个简单的PHP对象,它可以生成一个决断语句。 在现实生活中,一个约束可以是”蛋糕不能烤焦了” 这样的规则约束。在Symfony中,约束都差不多:他们断言某个条件是否成立。给定一个值,约束会告诉你这个值是否遵守了你的约束规则。
下列可以满足多数需求的约束,在Symfony中原生可用。
这些是基本的约束:使用它们来断言属性值相关的非常基础的东西,或者断言你程序中的方法之返回值。
你也可以创建自己的自定义约束。请查看“如何创建一个自定义的验证约束”。
一些约束,比如NotBlank,很简单,但是其它的比如Choice约束,有许多配置项需要设置。假设 Author
类有另外一个属性, gender
可以被设置为”male”、”female”或者“other”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // 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;
// ...
} |
1 2 3 4 5 6 | # src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\Author:
properties:
gender:
- Choice: { choices: [male, female, other], message: Choose a valid gender. }
# ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <!-- 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> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // 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.',
)));
}
} |
一个约束的选项通常都是通过一个数组来传递的。有些约束也允许你通过指定一个一个值,”default”,用这个配置来代替数组。在 Choice
约束时, choices
选项就可以通过这种方式指定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // src/AppBundle/Entity/Author.php
// ...
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\Choice({"male", "female", "other"})
*/
protected $gender;
// ...
} |
1 2 3 4 5 6 | # src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\Author:
properties:
gender:
- Choice: [male, female, other]
# ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!-- 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> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 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'))
);
}
} |
这纯粹是让最常见的配置选项用起来更加简单和快速。
如果你不确定如何指定一个配置,你可以检查api文档或者为了保险起见你还是通过数组配置吧(上面第一种方式)。
约束可以被用于一个类的属性(如, name
)或者一个公共的getter方法(如, getFullName
)。属性约束最常用也最简单,而公共的getter方法约束则允许你指定一个复杂的约束规则。最后,如果你想验证一个整体的类,类约束适用这个场景。
属性验证一个最常规的验证技术。Symfony允许你校验 private
, protected
或者 public
属性。下面代码显示如何配置 Author
对象的 $firstName
属性至少有3个字符:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // src/AppBundle/Entity/Author.php
// ...
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank()
* @Assert\Length(min=3)
*/
private $firstName;
} |
1 2 3 4 5 6 7 | # src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\Author:
properties:
firstName:
- NotBlank: ~
- Length:
min: 3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!-- 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> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 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))
);
}
} |
约束也可以应用于一个方法的返回值。Symfony允许你添加一个约束到任何”get”、”is”或者”has”开头的public方法。这种类型的方法被称为“getters”。
该技术的好处是允许你动态的校验你的对象。比如,假设你想确认密码字段不匹配用户的firstname(一个属性)(因为安全原因)。你可以通过创建一个 isPasswordLegal
方法,然后强制决断这个方法必须返回 true
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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
}
} |
1 2 3 4 5 | # src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\Author:
getters:
passwordLegal:
- 'IsTrue': { message: 'The password cannot match your first name' } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!-- 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> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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',
)));
}
} |
现在我们创建一个 isPasswordLegal()
方法,并且包含你需要逻辑:
1 2 3 4 | public function isPasswordLegal()
{
return $this->firstName !== $this->password;
} |
眼尖的人可能会注意到getter的前缀(“get”、”is”或者“has”)在映射时被忽略了。这允许你在不改变验证规则的前提下,把一个约束移动到一个具有同名属性上,反之亦然。
有一些约束可以应用到被验证的整个类。例如,回调(Callback)类型的约束,就是一个通用的,用于类自身的约束。当类被验证之后,约束所指定的方法将被直接执行,以便提供更多的自定义验证。
Symfony的 validator
(验证)是一个强大的工具,它可以被用来保证任何对象数据的合法性。它的强大来源于约束规则,你可以把它们应用于你对象的属性和getter方法。其实,你大多数情况下都是在使用表单时,间接的应用了验证框架,记住它可以被应用于任何地方验证任何对象。
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。