初识laravel5

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

目录结构变化

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

laravel5最先强调的是项目目录结构的变化,与4.2区别还是蛮大的,逐条来说吧。

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

新的目录结构看上去像这样:

基本验证

app
    Commands
    Console
    Events
    Handlers
        Commands
        Events
    Http
        Controllers
        Middleware
        Requests
        Kernel.php
        routes.php
    Providers
    Services
bootstrap
config
database
    migrations
    seeds
public
    package
resources
    lang
    views
storage
    cache
    logs
    meta
    sessions
    views
    work
tests

理解校验的最好方法是看它的表现。首先,假设你已经创建了一个用于你应用程序某个地方的PHP对象。
复制代码
代码如下://src/Acme/BlogBundle/Entity/Author.php
namespace AcmeBlogBundleEntity;

4.2的目录结构:

class Author
{
    public $name;
}

app
    commands
    config
    controllers
    database
    lang
    models
    start
    storage
    tests
    views
bootstrap
public
对比一下,改变还是比较大的,可以看到
config,database被移动到根目录,lang,views目录被移动到resources目录下,controllers被整合到http目录里,models目录不见了,还有一些新增的目录就略了。

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

App命名空间

YAML格式:
复制代码 代码如下:#
src/Acme/BlogBundle/Resources/config/validation.yml
AcmeBlogBundleEntityAuthor:
    properties:
         name:
            – NotBlank: ~
类声明格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/Author.php
use SymfonyComponentValidatorConstraints as Assert;

laravel5里还有一个变化,那就是app目录默认加上了一个根命名空间 App ,在
App 下的所有目录、类都应当在该命名空间下,简而言之就是采用了psr4标准。

class Author
{
   /**
    * @AssertNotBlank()
    */
    public $name;
}

HTTP

XML格式:
复制代码 代码如下:

laravel5认为,新的目录结构是目前最好的结构之一,可以让我们的开发更加得心应手,比如http目录:

    xmlns:xsi=””
    xsi:schemaLocation=”
;

Http
    Controllers
    Middleware
    Requests
    Kernel.php
    routes.php
Middleware
很陌生,其实它是原来的路由filter的一个升级版,现在不用在filters.php里定义过滤器,取而代之的是在
Middleware
目录中创建类,并在Kernel.php中配置全局还是可选,全局的Middleware在每个请求都会执行,而可选的就相当于原来的filter,可以在路由中使用,也可以在控制器中使用。

   
       
           
       
   

Requests是对核心类Request的扩展,你可以扩展不同的Requests类,添加不同的功能。

PHP代码格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/Author.php

可以认为,所有关于http请求有关的处理都在http目录中,比如控制器就是用来接受一个请求并返回的,所以将它放在
Http 目录里合情合理。

use SymfonyComponentValidatorMappingClassMetadata;
use SymfonyComponentValidatorConstraintsNotBlank;

路由

class Author
{
   public $name;
  
   public static function loadValidatorMetadata(ClassMetadata
$metadata)
   {
       $metadata->addPropertyConstraint(‘name’, new NotBlank());
   }
}

路由跟以前的区别不大,但是需要注意的当我们指定控制器命名空间时,命名空间不是绝对路径,而是相对于
AppHttpControllers,举例:

Protected和private属性以及getter方法也都可以被校验。

复制代码 代码如下:
Route::controllers([
    ‘auth’ => ‘AuthAuthController’,
    ‘password’ => ‘AuthPasswordController’,
]);

使用validator服务:

可以在 App/Http/Controllers/Auth 目录下找到对应的类。

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

此外,路由还支持缓存,以提升性能,通过命令行工具

public function indexAction()
{
   $author = new Author();
   //… 对$auother对象做些什么
  
   $validator = $this->get(‘validator’);
   $errors = $validator->validate($author);

复制代码 代码如下:
php artisan route:cache

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

即可轻松生成,也可以通过

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

复制代码 代码如下:
php artisan route:clear

AcmeBlogBundleAuthor.name:
     This value should not be blank

清理缓存。

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

Services

大多数时候,你不需要直接跟validator服务交流或者根本不需要担心打印出错误来。

我们看到在App目录下还有一个Services目录,我觉得这是一个很赞的理念,一直以来,我都对于控制器中出现大段的业务逻辑代码而烦躁,我很想用一个单独的层把这些业务逻辑封装起来,而services就可以用来干这个活,当然,它不是必须的,但我强烈建议使用。就以laravel5自带的demo来看看吧:

大多数情况下,你将在处理提交表单数据时间接使用校验。

复制代码 代码如下:
# Http/Controllers/Auth/AuthController.php
use AppHttpControllersController;
use IlluminateContractsAuthGuard;
use IlluminateContractsAuthRegistrar;
use IlluminateFoundationAuthAuthenticatesAndRegistersUsers;
class AuthController extends Controller {
    /*
   
|————————————————————————–
    | Registration & Login Controller
   
|————————————————————————–
    |
    | This controller handles the registration of new users, as well as
the
    | authentication of existing users. By default, this controller
uses
    | a simple trait to add these behaviors. Why don’t you explore it?
    |
    */
    use AuthenticatesAndRegistersUsers;
    /**
     * Create a new authentication controller instance.
     *
     * @param  IlluminateContractsAuthGuard  $auth
     * @param  Illuminate澳门新葡萄京官网首页 ,ContractsAuthRegistrar  $registrar
     * @return void
     */
    public function __construct(Guard $auth, Registrar $registrar)
    {
        $this->auth = $auth;
        $this->registrar = $registrar;
        $this->middleware(‘guest’, [‘except’ => ‘getLogout’]);
    }
}

你也可以传递一个错误信息集合到一个模版:
复制代码
代码如下:if(count($errors)>0){
   return
$this->render(‘AcmeBlogBundle:Author:validate.html.twig’,array(
        ‘errors’ => $errors,
   ));
}else{
  //…
}

这是一个登陆授权的控制器,我们看
__construct构造函数,利用参数自动注入了一个 “接口实现(参考手册IoC)”
的绑定,我们看下Registrar:

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

复制代码 代码如下:
use AppUser;
use Validator;
use IlluminateContractsAuthRegistrar as RegistrarContract;
class Registrar implements RegistrarContract {
    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return IlluminateContractsValidationValidator
     */
    public function validator(array $data)
    {
        return Validator::make($data, [
            ‘name’ => ‘required|max:255’,
            ’email’ => ‘required|email|max:255|unique:users’,
            ‘password’ => ‘required|confirmed|min:6’,
        ]);
    }
    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return User
     */
    public function create(array $data)
    {
        return User::create([
            ‘name’ => $data[‘name’],
            ’email’ => $data[’email’],
            ‘password’ => bcrypt($data[‘password’]),
        ]);
    }
}

Twig格式:
复制代码 代码如下:{#
src/Acme/BlogBundle/Resources/views/Author/validate.html.twig #}

提交用户名密码时的处理:

The author has the following errros

  • {{ error.message }}

校验和表单

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

public function updateAction(Request $request)
{
    $author = new AcmeBlogBundleEntityAuthor();
    $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格式:
复制代码 代码如下:

  

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
AcmeBlogBundleEntityAuthor:
    properties:
         gener:
            – Choice: { choices: [male, female], message: Choos a
valid gender. }
类声明格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/Author.php
use SymfonyComponentValidatorConstraints as Assert;

class Author
{
   /**
    * @AssertChoice(
    *     choices = {“male”,”female”},
    *     message = “Choose a valid gender.”
    * )
    */
    public $gender;
}

XML格式:
复制代码 代码如下:

    xmlns:xsi=””
    xsi:schemaLocation=”
;

   
       
           
               
                    male
                    female
               
                Choose a valid gender.
           
       
   

PHP代码格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/Author.php
use SymfonyComponentValidatorMappingClassMetadata;
use SymfonyComponentValidatorConstraintsNotBlank;

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
AcmeBlogBundleEntityAuthor:
    properties:
        gender:
            – Choice: [male, female]
类声明格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/Author.php
use SymfonyComponentValidatorConstraints as Assert;

class Author
{
    /**
     * @AssertChoice({“male”, “female”})
     */
    protected $gender;
}

XML格式:
复制代码 代码如下:

    xmlns:xsi=””
    xsi:schemaLocation=”
;

   
       
           
                male
                female
           
       
   

PHP格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/Author.php
use SymfonyComponentValidatorMappingClassMetadata;
use SymfonyComponentValidatorConstraintsChoice;

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
AcmeBlogBundleEntityAuthor:
    properties:
        firstName:
            – NotBlank: ~
            – MinLength: 3
类声明格式:
复制代码 代码如下://
Acme/BlogBundle/Entity/Author.php
use SymfonyComponentValidatorConstraints as Assert;

class Author
{
    /**
     * @AssertNotBlank()
     * @AssertMinLength(3)
     */
    private $firstName;
}

XML格式:
复制代码 代码如下:

   
       
        3
   

PHP代码格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/Author.php
use SymfonyComponentValidatorMappingClassMetadata;
use SymfonyComponentValidatorConstraintsNotBlank;
use SymfonyComponentValidatorConstraintsMinLength;

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
AcmeBlogBundleEntityAuthor:
    getters:
        passwordLegal:
            – “True”: { message: “The password cannot match your first
name” }
类声明格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/Author.php
use SymfonyComponentValidatorConstraints as Assert;

class Author
{
    /**
     * @AssertTrue(message = “The password cannot match your first
name”)
     */
    public function isPasswordLegal()
    {
        // return true or false
    }
}

XML格式:
复制代码 代码如下:

   
       
            The password cannot match your first name
       
   

PHP代码格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/Author.php
use SymfonyComponentValidatorMappingClassMetadata;
use SymfonyComponentValidatorConstraintsTrue;

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
AcmeBlogBundleEntityUser:
    properties:
        email:
            – Email: { groups: [registration] }
        password:
            – NotBlank: { groups: [registration] }
            – MinLength: { limit: 7, groups: [registration] }
        city:
            – MinLength: 2

类声明格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/User.php
namespace AcmeBlogBundleEntity;

use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentValidatorConstraints as Assert;

class User implements UserInterface
{
    /**
    * @AssertEmail(groups={“registration”})
    */
    private $email;

    /**
    * @AssertNotBlank(groups={“registration”})
    * @AssertMinLength(limit=7, groups={“registration”})
    */
    private $password;

    /**
    * @AssertMinLength(2)
    */
    private $city;
}

XML格式:
复制代码 代码如下:

   
       
           
                registration
           
       
   
   
       
           
                registration
           
       
       
            7
           
                registration
           
       
   
   
        7
   

PHP代码格式:
复制代码 代码如下://
src/Acme/BlogBundle/Entity/User.php
namespace AcmeBlogBundleEntity;

use SymfonyComponentValidatorMappingClassMetadata;
use SymfonyComponentValidatorConstraintsEmail;
use SymfonyComponentValidatorConstraintsNotBlank;
use SymfonyComponentValidatorConstraintsMinLength;

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 SymfonyComponentValidatorConstraintsEmail;

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框架程序设计有所帮助。

复制代码 代码如下:
public function postRegister(Request $request)
{
    $validator =
$this->registrar->validator($request->all());
    if ($validator->fails())
    {
        $this->throwValidationException(
            $request, $validator
        );
    }
   
$this->auth->login($this->registrar->create($request->all()));
    return redirect($this->redirectPath());
}

可以看到,表单验证的业务逻辑仅仅一行:

复制代码 代码如下:
$validator = $this->registrar->validator($request->all());

整个控制器的代码显得干净易读,我们可以把很多通用的业务逻辑封装成service,比不伦不类地直接封装在控制器类好。

模型

models目录不见了,因为不是所有应用都需要用到数据库的,所以laravel5默认不提供该目录可以理解,而且由于提供了
App 这个namespace,所以我们可以自己在 App/ 下创建 Models
目录,其中所有模型类都声名namespace
AppModels;即可,只是使用上比以前麻烦一些,需要先use,不过这样也使得项目结构更加清晰,一切类库都在命名空间的组织之下。

时间有限,先写这么多吧。希望大家能够喜欢。

发表评论

电子邮件地址不会被公开。 必填项已用*标注