Symfony2学习笔记之数据校验

校验在web应用程序中是一个常见的任务。数据输入到表单需要被校验。数据在被写入数据库之前或者传入一个webservice时也需要被校验。

Symfony2 配备了一个Validator 组件,它让校验工作变得简单易懂。该组件是基于JSR303 Bean校验规范。一个Java规范用在PHP中。

基本验证
理解校验的最好方法是看它的表现。首先,假设你已经创建了一个用于你应用程序某个地方的PHP对象。

//src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;

class Author
{
    public $name;
}

到现在为止,它只是个服务于你应用程序的某些目的的普通的类。而校验的目的就是要告诉你对象的数据是否合法。为了这个目的,你需要配置一个对象必须遵守规则或者约束列表来让自己的数据合法。这些规则可以被描述成多种不同的格式的(比如,YAML,XML,类声明或者PHP)。比如,我们保证属性$name不能为空,来添加下面的规则:
YAML格式:

# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
    properties:
         name:
            - NotBlank: ~

类声明格式:

// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
   /**
    * @Assert\NotBlank()
    */
    public $name;
}

XML格式:

<!-- src/Acme/BlogBundle/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="Acme\BlogBundle\Entity\Author">
        <property name="name">
            <constraint name="NotBlank" />
        </property>
    </class>
</constraint-mapping>

PHP代码格式:

// src/Acme/BlogBundle/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方法也都可以被校验。

使用validator服务
接下来,使用validator服务的validate方法来真正的校验Author对象。 validator的工作很简单:读取一个类的约束规则来校验一个对象的数据是否符合这些规则约束。如果校验失败,一个错误数组将被返回。现在我们在一个controller中来执行它:

use Symfony\Component\HttpFoundation\Response;
use Acme\BlogBundle\Entity\Author;
//...

public function indexAction()
{
   $author = new Author();
   //... 对$auother对象做些什么

   $validator = $this->get(‘validator‘);
   $errors = $validator->validate($author);

   if(count($errors) >0){
     return new Response(print_r($errors, true));
   }else{
     return new Response(‘The author is valid! Yes!‘);
   }
}

如果$name 属性为空,你将看到下面的错误信息:

Acme\BlogBundle\Author.name:
     This value should not be blank

如果你为$name属性插入一个值,那么你会获得快乐的成功信息。

大多数时候,你不需要直接跟validator服务交流或者根本不需要担心打印出错误来。
大多数情况下,你将在处理提交表单数据时间接使用校验。

你也可以传递一个错误信息集合到一个模版:

if(count($errors)>0){
   return $this->render(‘AcmeBlogBundle:Author:validate.html.twig‘,array(
        ‘errors‘ => $errors,
   ));
}else{
  //...
}

在模版中,你可以根据需要精确的输出错误列表:
Twig格式:

{# src/Acme/BlogBundle/Resources/views/Author/validate.html.twig #}
<h3>The author has the following errros</h3>
<ul>
{% for error in errors %}
    <li>{{ error.message }}</li>
{% endfor %}
</ul>

校验和表单
validator服务可以被用于任何时候校验任何对象。 事实上,你将经常在处理表单时间接使用validator。Symfony的表单类库间接使用validator服务来在数据被提交和绑定后校验底层对象。对象违反约束信息将被转化到FieldError对象,该对象可以很容易的被展示在你的表单中。在一个controller中的传统表单提交流程如下:

use Acme\BlogBundle\Entity\Author;
use Acme\BlogBundle\Form\AuthorType;
use Acme\Component\HttpFoundation\Request;
//...

public function updateAction(Request $request)
{
    $author = new Acme\BlogBundle\Entity\Author();
    $form = $this->createForm(new AuthorType(),$author);

    if($request->getMethod() ==‘POST‘){
       $form->bindRequest($request);

       if($form->isvalid()){
          //对$author做一些操作
          return $this->redirect($this->generateUrl(‘...‘));
       }
     }

     return $this->render(‘BlogBundle:Author:form.html.twig‘,array(
         ‘form‘ => $form->createView(),
     ));
}

配置
Symfony2 的validator默认情况下是可用的。但是如果你使用了生命方法来指定你的约束,那么你需要显式的开启声明功能:
YAML格式:

# app/config/config.yml
framework:
   validation: {enable_annotations: true }

XML格式:

<!-- app/config/config.xml -->
<framework:config>
   <framework:validation enable-annotations="true" />
</framework:config>

PHP代码格式:

// app/config/config.php
$contianer->loadFromExtension(‘framework‘,array(‘validation‘=> array(
     ‘enable_annotations‘=>true,
)));

约束规则
Validator是设计了用来按照约束规则校验对象的。为了校验一个对象,只需要映射一个或者多个约束到它要校验的类然后把它传递给validator服务即可。

本质上,一个约束就是一个简单的PHP对象,它可以生成一个决断语句。 在现实生活中,一个约束可以是"蛋糕不能烤焦了" 这样的规则约束。在Symfony2中,约束都差不多:他们决断某个条件是否成立。给定一个值,约束会告诉你这个值是否遵守了你的约束规则。

Symfony2 支持的约束规则
首先是基础约束规则:使用他们来决断非常基本的事,比如你对象属性的值或者方法的返回值。
NotBlank,Blank,NotNull,Null,True,False,Type

字符串约束:Email,MinLength,MaxLength,Url,Regex,Ip等
数字约束:Max,Min
日期约束:Date,DateTime和Time
集合约束:Choice,Collection,UniqueEntity,Language,Locale和Country等。
文件约束:File,Image
其它约束:Callback,All,Valid
你也可以创建自己的自定义约束。

约束配置
一些约束,比如NotBlank,很简单,但是其它的比如Choice约束,有许多配置项需要设置。假设Author类有另外一个属性,gener可以被设置为”male"或者"female":
YAML格式:

# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
    properties:
         gener:
            - Choice: { choices: [male, female], message: Choos a valid gender. }

类声明格式:

// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
   /**
    * @Assert\Choice(
    *     choices = {"male","female"},
    *     message = "Choose a valid gender."
    * )
    */
    public $gender;
}

XML格式:

<!-- src/Acme/BlogBundle/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="Acme\BlogBundle\Entity\Author">
        <property name="gender">
            <constraint name="Choice">
                <option name="choices">
                    <value>male</value>
                    <value>female</value>
                </option>
                <option name="message">Choose a valid gender.</option>
            </constraint>
        </property>
    </class>
</constraint-mapping>

PHP代码格式:

// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;

class Author
{
    public $gender;

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint(‘gender‘, new Choice(array(
            ‘choices‘ => array(‘male‘, ‘female‘),
            ‘message‘ => ‘Choose a valid gender.‘,
        )));
    }
}

一个约束的选项通常都是通过一个数组来传递的。有些约束也允许你传递一个值。"default"在数组中是可选的。在Choice约束时,choices选项就可以通过这种方式指定。
YAML格式:

# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
    properties:
        gender:
            - Choice: [male, female]

类声明格式:

// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
    /**
     * @Assert\Choice({"male", "female"})
     */
    protected $gender;
}

XML格式:

<!-- src/Acme/BlogBundle/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="Acme\BlogBundle\Entity\Author">
        <property name="gender">
            <constraint name="Choice">
                <value>male</value>
                <value>female</value>
            </constraint>
        </property>
    </class>
</constraint-mapping>

PHP格式:

// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\Choice;

class Author
{
    protected $gender;

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint(‘gender‘, new Choice(array(‘male‘, ‘female‘)));
    }
}

约束目标
约束可以被用于一个类的属性或者一个公共的getter方法。属性约束最常用也最简单,而公共的getter方法约束则允许你指定一个复杂的约束规则。

属性约束
校验类的属性石一个最常规的校验技术。Symfony2允许你校验private,protected或者public属性。下面代码显示如何配置Author对象的$firstName属性至少有3个字符:
YAML格式:

# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
    properties:
        firstName:
            - NotBlank: ~
            - MinLength: 3

类声明格式:

// Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
    /**
     * @Assert\NotBlank()
     * @Assert\MinLength(3)
     */
    private $firstName;
}

XML格式:

<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\Author">
    <property name="firstName">
        <constraint name="NotBlank" />
        <constraint name="MinLength">3</constraint>
    </property>
</class>

PHP代码格式:

// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\MinLength;

class Author
{
    private $firstName;

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint(‘firstName‘, new NotBlank());
        $metadata->addPropertyConstraint(‘firstName‘, new MinLength(3));
    }
}

Getters
约束也可以应用于一个方法的返回值。Symfony2 允许你添加一个约束到任何"get"或者 "is"开头的public方法。该技术的好处是允许你动态的校验你的对象。比如,假设你想确认密码字段不匹配用户的first name(因为安全原因)。你可以通过创建一个idPasswordLegal 方法,然后决断这个方法必须返回true:
YAML格式:

# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
    getters:
        passwordLegal:
            - "True": { message: "The password cannot match your first name" }

类声明格式:

// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
    /**
     * @Assert\True(message = "The password cannot match your first name")
     */
    public function isPasswordLegal()
    {
        // return true or false
    }
}

XML格式:

<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\Author">
    <getter property="passwordLegal">
        <constraint name="True">
            <option name="message">The password cannot match your first name</option>
        </constraint>
    </getter>
</class>

PHP代码格式:

// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\True;

class Author
{
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addGetterConstraint(‘passwordLegal‘, new True(array(
            ‘message‘ => ‘The password cannot match your first name‘,
        )));
    }
}

现在我们创建一个isPasswordLegal()方法,并且包含你需要逻辑:

public function isPasswordLegal()
{
   return ($this->firstName != $this->password);
}

眼尖的人可能会注意到getter的前缀("get"或者"is")在映射时被忽略了。这允许你在不改变校验规则的前提下,把一个约束移动到一个具有同名属性上,反之亦然。


一些约束应用到整个类被校验上面。比如,Callback约束是一个通用约束,它可以应用到类自身。当类被校验时,被约束描述的方法只是被执行这样每一个可以提供更个性化的校验。

校验分组
到目前为止,你已经能够添加约束到类并询问是否该类传入所有定义的约束规则。一些情况下,你只需要使用该类的其中某些规则来校验一个对象。要做到这些,你可以组织每一个约束到一个或者多个校验组中,然后应用使用其中一组校验。比如,假设你有一个User类,它会在用户注册和用户更新他们的联系信息时使用。
YAML格式:

# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\User:
    properties:
        email:
            - Email: { groups: [registration] }
        password:
            - NotBlank: { groups: [registration] }
            - MinLength: { limit: 7, groups: [registration] }
        city:
            - MinLength: 2

类声明格式:

// src/Acme/BlogBundle/Entity/User.php
namespace Acme\BlogBundle\Entity;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;

class User implements UserInterface
{
    /**
    * @Assert\Email(groups={"registration"})
    */
    private $email;

    /**
    * @Assert\NotBlank(groups={"registration"})
    * @Assert\MinLength(limit=7, groups={"registration"})
    */
    private $password;

    /**
    * @Assert\MinLength(2)
    */
    private $city;
}

XML格式:

<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\User">
    <property name="email">
        <constraint name="Email">
            <option name="groups">
                <value>registration</value>
            </option>
        </constraint>
    </property>
    <property name="password">
        <constraint name="NotBlank">
            <option name="groups">
                <value>registration</value>
            </option>
        </constraint>
        <constraint name="MinLength">
            <option name="limit">7</option>
            <option name="groups">
                <value>registration</value>
            </option>
        </constraint>
    </property>
    <property name="city">
        <constraint name="MinLength">7</constraint>
    </property>
</class>

PHP代码格式:

// src/Acme/BlogBundle/Entity/User.php
namespace Acme\BlogBundle\Entity;

use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\MinLength;

class User
{
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint(‘email‘, new Email(array(
            ‘groups‘ => array(‘registration‘)
        )));

        $metadata->addPropertyConstraint(‘password‘, new NotBlank(array(
            ‘groups‘ => array(‘registration‘)
        )));
        $metadata->addPropertyConstraint(‘password‘, new MinLength(array(
            ‘limit‘  => 7,
            ‘groups‘ => array(‘registration‘)
        )));

        $metadata->addPropertyConstraint(‘city‘, new MinLength(3));
    }
}

这里我们配置了两个校验组:
      default默认组: 包括所有没有分配到任何组的约束规则
      registration: 只包含了email和password字段的校验规则

告诉validator使用指定的校验组,传一个或者多个组名作为validate()方法的第二个参数即可:

$errors = $validator->validate($author,array(‘registration‘));

值和数组校验
到目前为止,我们已经看了如何校验整个对象。但是有时候,我们可能想值校验一个单独的值,比如校验一个字符串是不是一个合法的email地址。这非常简单,在Controller类中进行如下:

// 在controller类前引用相应的校验命名空间
use Symfony\Component\Validator\Constraints\Email;

public function addEmailAction($email)
{
    $emailConstraint = new Email();
    // 所有的校验选项(options)都可以这样设置
    $emailConstraint->message = ‘Invalid email address‘;

    // 使用validator来校验一个值
    $errorList = $this->get(‘validator‘)->validateValue($email, $emailConstraint);

    if (count($errorList) == 0) {
        // 这是一个合法的email地址,可以做些什么
    } else {
        // 这是一个非法的email地址
        $errorMessage = $errorList[0]->getMessage()

        // 做一些错误处理
    }

    // ...
}

通过调用validator的validateValue方法,你可以传入一个原始值和一个你要使用的校验对象。该方法会返回一个ConstraintViolationList对象,它扮演的只是一个错误信息数组的角色。集合中的每一个错误是一个ConstraintViolation对象,使用对象的getMessage方法可以获取错误信息。

总结
Symfony2 的validator是一个强大的工具,它可以被用来保证任何对象数据的合法性。它的强大来源于约束规则,你可以把它们应用于你对象的属性和getter方法。其实,你大多数情况下都是在使用表单时,间接的应用了校验框架,记住它可以被应用于任何地方校验任何对象。

参考URL:http://symfony.com/doc/current/book/validation.html

Symfony2学习笔记之数据校验

时间: 2024-11-11 04:56:09

Symfony2学习笔记之数据校验的相关文章

Struts2学习笔记(九)——数据校验

Struts2的数据校验属于服务器端校验,Struts2 支持校验方式 : 手动校验(代码校验) :在服务器端通过编写java代码,完成数据校验 自动校验(配置校验) :XML配置校验(主流) 和 注解配置校验 1.手动校验 1)Struts2的手动校验步骤: 首先要从页面中获取对应的标签name属性的值,在动作类action中声明同名的属性,提供get和set方法; 要继承ActionSupport类或者实现Validateable接口: 重写Validateable接口的validate()

Symfony2学习笔记之HTTP Cache

富web应用程序的本质意味着它们的动态.无论你的应用程序多么有效率,每个请求比起静态文件来说总会存在很多的耗费.对于大多数web程序来说,这没什么. Symfony2非常的轻快,无论你做些严重超载的请求,每个请求将会得到很快的回复,而不会对你的服务器造成压力.但是随着你站点的成长,负载将成为一个严重的问题.对每个请求处理应该只被正常执行一次.这就是缓存真正要达成的目标. 站在巨人肩膀上的缓存:提高一个应用程序执行效率的最有效方法是缓存一个页面的所有输出然后让后续的请求绕开整个应用程序.当然,这对

Symfony2学习笔记之表单

对于一个Web开发者来说,处理HTML表单是一个最为普通又具挑战的任务.Symfony2集成了一个Form组件,让处理表单变的容易起来.在这一节里,我们将从基础开始创建一个复杂的表单,学习表单类库中最重要的内容. Symfony2 的Form组件是一个独立的类库,你可以在Symfony2项目之外使用它. 创建一个简单的表单:假设你要创建一个应用程序的todo列表,需要显示一些任务.因为你的用户需要编辑和创建任务,所以你需要创建一个表单.在你开始之前,首先来看通用的Task类,用来表示和存储一个单

Symfony2 学习笔记之服务容器

现在的PHP应用程序都是面向对象开发,所以主要是由对象构成.有的对象可以方便的分发邮件信息而有的可能帮你把信息写入到数据库中.在你的应用程序中,你可能创建一个对象用于管理你的产品库存,或者另外一个对象处理来自第三方API的数据.重要的是现在应用程序要做的这些事情都是被组织到许许多多的对象中来处理它的每一项任务的. 我们将套路一下Symfony2中一个特殊的PHP对象,它帮助我们实例化,组织和获取你应用程序汇总的许多对象.这个对象叫做服务容器,它可以帮助你使用标准统一的方式来创建你程序中的对象.它

Symfony2学习笔记之数据库操作

数据库和Doctrine让我们来面对这个对于任何应用程序来说最为普遍最具挑战性的任务,从数据库中读取和持久化数据信息.幸运的是,Symfony和Doctrine进行了集成,Doctrine类库全部目标就是给你一个强大的工具,让你的工作更加容易. Doctrine是完全解耦与Symfony的,所以并不一定要使用它. 一个简单例子:一个产品,我们首先来配置数据库,创建一个Product对象,持久化它到数据库并把它读回来. 首先我们需要创建一个bundle: $php app/console gene

Symfony2 学习笔记之内部构件

Symfony2内部是怎样工作的以及我们如何来扩展它呢?从外部整体上看,symfony2代码是由许多独立的层构成,每一层都是建立在前一层基础之上.其中,自动加载时不受框架直接管理的,它完全是在UniversalClassLoader类和src/autoload.php文件的帮助下独立完成的. HttpFoundation 组件最深层次的是HttpFoundation组件,它提供了处理HTTP所需的主要对象.是一个对一些PHP函数和变量的面向对象抽象.包括:Request 类,抽象了PHP中主要的

Symfony2学习笔记之事件分配器

----EventDispatcher组件使用 简介:       面向对象编程已经在确保代码的可扩展性方面走过了很长一段路.它是通过创建一些责任明确的类,让它们之间变得更加灵活,开发者可以通过继承这些类创建子类,来改变它们的行为.但是如果想将某个开发者的改变跟其它已经编写了自己子类的开发者共享,这种面向对象的继承就不再那么有用了. 举一个现实的实例,你想为你的项目提供一个插件系统.一个能够被添加到方法的插件,或者在方法执行的前后完成某些工作,而不会干扰到其它插件.这个通过单一的继承完成不是一个

python 学习笔记 3 -- 数据结构篇上

数据结构是可以处理一些 数据 的 结构 .或者说,它们是用来存储一组相关数据的.在Python中有三种内建的数据结构--列表.元组和字典.本文主要对这三种数据类型以及相关的使用做介绍,以例子的形式演示更加容易理解! 1.列表(List) 列表是处理一组有序项目的数据结构,即你可以在一个列表中存储一个 序列 的项目.在Python中,你在每个项目之间用逗号分割. 列表中的项目应该包括在**方括号**中,这样Python就知道你是在指明一个列表.一旦你创建了一个列表,你可以添加.删除或是搜索列表中的

python 学习笔记 3 -- 数据结构篇下

5.引用 当你创建一个对象并给它赋一个变量的时候,这个变量仅仅 引用 那个对象,而不是表示这个对象本身!也就是说,变量名指向你计算机中存储那个对象的内存.这被称作名称到对象的绑定.eg. [python] view plaincopy # -*- coding: utf-8 -*- shoplist = ['apple', 'mango', 'carrot', 'banana'] print "we copy the shoplist to mylist directly \"with