Symfony数据校验方法实例分析


Posted in PHP onJanuary 26, 2015

本文实例讲述了Symfony数据校验方法。分享给大家供大家参考。具体分析如下:

校验在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方法。其实,你大多数情况下都是在使用表单时,间接的应用了校验框架,记住它可以被应用于任何地方校验任何对象。

希望本文所述对大家的Symfony框架程序设计有所帮助。

PHP 相关文章推荐
php实现12306余票查询、价格查询示例
Apr 17 PHP
理解PHP中的stdClass类
Apr 18 PHP
php获取服务器端mac和客户端mac的地址支持WIN/LINUX
May 15 PHP
php保存任意网络图片到服务器的方法
Apr 14 PHP
php利用smtp类实现电子邮件发送
Oct 30 PHP
php类中的$this,static,final,const,self这几个关键字使用方法
Dec 14 PHP
thinkPHP实现MemCache分布式缓存功能
Mar 23 PHP
php文件管理基本功能简单操作
Jan 16 PHP
深入讲解PHP的对象注入(Object Injection)
Mar 01 PHP
php实现的二叉树遍历算法示例
Jun 15 PHP
php+redis实现商城秒杀功能
Nov 19 PHP
PHP实现的策略模式示例
Mar 20 PHP
symfony表单与页面实现技巧
Jan 26 #PHP
php使用cookie保存登录用户名的方法
Jan 26 #PHP
Symfony页面的基本创建实例详解
Jan 26 #PHP
PHP中使用imagick生成PSD文件缩略图教程
Jan 26 #PHP
PHP中使用imagick实现把PDF转成图片
Jan 26 #PHP
PHP中使用Imagick操作PSD文件实例
Jan 26 #PHP
PHP实现将浏览历史页面网址保存到cookie的方法
Jan 26 #PHP
You might like
利用递归把多维数组转为一维数组的函数
2006/10/09 PHP
PHP+MySQL 手工注入语句大全 推荐
2009/10/30 PHP
探讨PHP删除文件夹的三种方法
2013/06/09 PHP
浅析php工厂模式
2014/11/25 PHP
php简单实现短网址(短链)还原的方法(测试可用)
2016/05/09 PHP
Yii2使用dropdownlist实现地区三级联动功能的方法
2016/07/18 PHP
thinkphp5.1框架模板布局与模板继承用法分析
2019/07/19 PHP
jQuery 操作下拉列表框实现代码
2010/02/22 Javascript
IE下写xml文件的两种方式(fso/saveAs)
2013/08/05 Javascript
js data日期初始化的5种方法
2013/12/29 Javascript
让table变成exls的示例代码
2014/03/24 Javascript
js数组的操作指南
2014/12/28 Javascript
jQuery xml字符串的解析、读取及查找方法
2016/03/01 Javascript
纯JS代码实现一键分享功能
2016/04/20 Javascript
JavaScript代码实现图片循环滚动效果
2020/03/19 Javascript
AngularJS出现$http异步后台无法获取请求参数问题的解决方法
2016/11/03 Javascript
AngularJS前端页面操作之用户修改密码功能示例
2017/03/27 Javascript
vue动态路由实现多级嵌套面包屑的思路与方法
2017/08/16 Javascript
vue2.0使用swiper组件实现轮播效果
2017/11/27 Javascript
详解一个基于react+webpack的多页面应用配置
2019/01/21 Javascript
react用Redux中央仓库实现一个todolist
2019/09/29 Javascript
[11:33]DAC2018 4.5SOLO赛决赛 MidOne vs Paparazi第二场
2018/04/06 DOTA
[01:12:53]完美世界DOTA2联赛PWL S2 Forest vs SZ 第一场 11.25
2020/11/26 DOTA
Python中max函数用法实例分析
2015/07/17 Python
Python实现基本数据结构中栈的操作示例
2017/12/04 Python
机器学习之KNN算法原理及Python实现方法详解
2018/07/09 Python
python数据预处理之数据标准化的几种处理方式
2019/07/17 Python
详解Python用三种方式统计词频的方法
2019/07/29 Python
pymysql 开启调试模式的实现
2019/09/24 Python
Django数据库迁移常见使用方法
2020/11/12 Python
HTML5中微数据概述及在搜索引擎中的使用举例
2013/02/07 HTML / CSS
香港中原电器网上商店:Chung Yuen
2019/06/26 全球购物
商场拾金不昧表扬信
2014/01/13 职场文书
酒店节能降耗方案
2014/05/08 职场文书
政府领导干部个人对照检查材料思想汇报
2014/09/24 职场文书
2016年小学生新年寄语
2015/08/18 职场文书