Yii2的表单验证之二:服务器端验证

Yii2具有强大的表单验证功能,能用好表单验证,用户输入就基本掌握了,在这里我和各位聊聊Yii2的服务器端验证器

所谓服务器端验证器,就是在modelAR(Active Record)中定义,使用validate()save()函数进行验证的验证器。
关于输入验证Yii2官网文档是这样说的:

As a rule of thumb, you should never trust the data received from end users and should always validate it before putting it to good use.  
摘自:http://www.yiiframework.com/doc-2.0/guide-input-validation.html

这句话的意思是任何从客户端过来的数据都要进行验证!
这里所说的验证主要是指服务器端验证,所以说服务器端验证在输入处理过程中是必不可少的!

服务器端验证代码结构大概是这样:

if($model->validate()){//或if($model->save()){
    //验证通过后执行的代码,如数据存储等
}else{
    //显示验证错误信息
    $this->errorDisplay($model->getErrors());
}

这段代码通常是存放在ControllerAction中的,请参见后面的源码。

在服务器端进行数据验证通常有两种调用方式:
1、$model->validate()
此方法将根据rules()中定义的验证规则对所有数据进行有效性检测,检测通过,则进行数据存储等后续操作;验证未通过则显示错误信息。$this->errorDisplay()调用自CommonController,请参见:
http://www.jianshu.com/p/5d2c42166702
2、$model->save()
此方法在AR(Active Record)中经常使用到,它默认调用了$model->validate(),验证通过后则直接进行数据存储,返回true;验证失败则不进行数据存储,返回false,错误信息存储在$model->errors中。顺便提一下,$model->save(false)是AR不进行数据验证的存储操作,通常嵌套于$model->validate()通过后的内层,即:

if($model->validate()){//此处的$model是一个Active Record(AR)
    //AR验证通过,存储时无需再进行二次验证
    if ($model->save(false)) {
        //数据存储成功
    }else{
        return $this->error('Sorry,Data save fail!');
    }
}

我们先来看一看服务器端验证的效果是什么样子?

31.jpg

上图是客户端验证的效果,即,将ActiveForm的enableClientValidation设置为true时的验证效果:验证通过则输入框变为绿色,验证未通过则输入框变为红色并在其下方显示错误信息。
32.jpg

上图是服务器端验证的效果,即,将ActiveForm的enableClientValidation设置为false时,由$model->validate()验证失败后显示出来的错误信息页面。
注意:要显示上图页面效果,请确保你的YII_DEBUG=true

在此需要说明的是:
1、为了展现服务器端验证的效果,所以在此专门把enableClientValidation设置为false,开发时无需进行此项设置。
2、只要调用$model->validate()或AR的$model->save()方法,Yii2就会进行服务器端的数据验证,无需做额外的参数设置。

服务器端验证的规则在哪里编写呢?与客户端验证一样,服务器端验证的规则也是在rules()中编写。而且,一条同样的验证规则,只需要编写一次就可以了,Yii2会自动在客户端服务器端以及Ajax验证中自行调用,是不是感觉Yii2框架很神奇呢!在马化腾写程序的年代可是要在三处分别编写代码的:(

我们一起来看一看rules()中验证规则,请仔细阅读代码中的注释

    public function rules() {
        return [
            //三项都是必填项(required)
            [['countryName','countryAbbr','continent'],'required'],
            //对表单项进行去掉首尾空格的处理(trim)
            [['countryName','countryAbbr','continent'],'trim'],
            //对'countryName'进行唯一性检测(unique),即此项输入值在数据库是唯一的(不存在此值),否则报错
            ['countryName', 'unique',
                //设置此项要使用AR(Active Record)类,只有AR类才可以对数据库直接进行查询,Model默认是没有此功能的
                'targetClass' => '\frontend\models\Country',
                //本输入项'countryName'对应查询的是Country表中的name字段
                'targetAttribute' => 'name',
                //使用了一个过滤器,即andWhere(['continent' => 'Europe'])
                'filter' => ['continent' => 'Europe'],
                //验证未通过时要显示的信息
                'message' => '输入的国家名称已在Europe中存在!',
            ],
            //对'countryName'进行存在检测(exist),即此项输入值在数据库是存在的,否则报错
            ['countryAbbr', 'exist',
                'targetClass' => '\frontend\models\Country',
                'targetAttribute' => 'code',
                'filter' => ['continent' => 'Europe'],
                'message' => 'Europe没有这样的国家简称',
            ],
            //对'continent'进行范围检测,只能输入所列范围(range)5项中的之一项,否则报错
            ['continent','in','range'=>['Europe','Asia','South America','North America','Oceania'],'message' => '没有这样的大洲'],
        ];
    }

本例中使用到的requiredtrimuniqueexist,都是Yii2提供的验证器的别名(简称),想要知道这些验证器的全称,可以调用以下代码:

    $aa=\yii\validators\Validator::$builtInValidators;
    var_dump($aa);

Yii2提供了23个核心验证器,请参阅文档:
http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html

本文所涉及到的程序源码

文件位置:D:\phpwork\advanced\frontend\controllers\DemoController.php

<?php
namespace frontend\controllers;
use Yii;
class DemoController extends CommonController{
     public function actionForm2() {
        $model=new \frontend\models\Form2();
        if ($model->load(Yii::$app->request->post())) {
            if (Yii::$app->request->isAjax) {
                Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
                return \yii\bootstrap\ActiveForm::validate($model);
            }
            if($model->validate()){
                if ($model->save()) {
                    return $this->render('form2a',['model'=>$model]);
                }else{
                    return $this->error('Sorry,Data save fail!');
                }
            }else{
                return $this->errorDisplay($model->getErrors());
            }
        }else{
            $countries=\frontend\models\Country::find()->asArray()->all();
            return $this->render('form2',['model'=>$model,'countries'=>$countries]);
        }
    }
}

其中errorDisplay()方法是调用自CommonController,请参见:
http://www.jianshu.com/p/5d2c42166702

文件位置:D:\phpwork\advanced\frontend\models\Form2.php

<?php
namespace frontend\models;
use Yii;
class Form2 extends \yii\base\Model{
    public $countryName,$countryAbbr,$continent;
    public function attributeLabels() {
        return [
            'countryName'=>'国家名称',
            'countryAbbr'=>'国家简称',
            'continent'=>'所属大洲'
        ];
    }
    public function rules() {
        return [
            //三项都是必填项
            [['countryName','countryAbbr','continent'],'required'],
            //对表单项进行去掉首尾空格的处理
            [['countryName','countryAbbr','continent'],'trim'],
            //对'countryName'进行唯一性检测(unique),即此项输入值在数据库是唯一的(不存在此值),否则报错
            ['countryName', 'unique',
                //设置此项要使用AR(Active Record)类,只有AR类才可以对数据库直接进行查询,Model默认是没有此功能的
                'targetClass' => '\frontend\models\Country',
                //本输入项'countryName'对应查询的是Country表中的name字段
                'targetAttribute' => 'name',
                //使用了一个过滤器,即andWhere(['continent' => 'Europe'])
                'filter' => ['continent' => 'Europe'],
                //验证未通过时要显示的信息
                'message' => '输入的国家名称已在Europe中存在!',
            ],
            //对'countryName'进行存在检测(exist),即此项输入值在数据库是存在的,否则报错
            ['countryAbbr', 'exist',
                'targetClass' => '\frontend\models\Country',
                'targetAttribute' => 'code',
                'filter' => ['continent' => 'Europe'],
                'message' => 'Europe没有这样的国家简称',
            ],
            //对'continent'进行范围检测,只能输入所列范围(range)5项中的之一项,否则报错
            ['continent','in','range'=>['Europe','Asia','South America','North America','Oceania'],'message' => '没有这样的大洲'],
        ];
    }

    public function save() {
        return true;
    }
}

文件位置:D:\phpwork\advanced\frontend\views\demo\form2.php

<?php
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;

?>
    <div class="panel panel-warning">
        <div class="panel-heading">
            <div class="panel-title">
                二、Yii2的表单验证——服务器端验证的验证器
            </div>
        </div>
        <div class="panel-body">
            <div class="row">
                <div class="col-md-7">
                    <?php
                    $form = ActiveForm::begin([
                        'enableClientValidation' => false,
//                        'enableClientValidation' => true,
                        //'enableAjaxValidation'=>false,
//                        'enableAjaxValidation'=>true,
                        'layout' => 'horizontal',
                    ]);
                    ?>
                    <?=$form->field($model,'countryName')->textInput()?>
                    <?=$form->field($model,'countryAbbr',['inputOptions'=>['class'=>'form-control']])->textInput()?>
                    <?=$form->field($model,'continent')->textInput()?>
                    <div class="row">
                        <div class='col-md-2 col-md-offset-2'><?= Html::submitButton('提     交 ', ['class' => 'btn btn-primary form-control']) ?></div>
                        <div class='col-md-2 col-md-offset-2'><?= Html::resetButton('重     置 ', ['class' => 'btn btn-default form-control'])?></div>
                    </div>
                    <?php ActiveForm::end(); ?>
                </div>
                <div class="col-md-5">
                    <TABLE class="table table-bordered country">
                        <TR >
                            <TD style="width:60px;">序号</TD>
                            <TD>国家</TD>
                            <TD style="width:60px;">简称</TD>
                            <TD>大洲</TD>
                        </TR>
                        <?php
                        foreach ($countries as $k=>$item) {
                            echo '<TR><TD>'.($k+1).'</TD><TD>'.$item['name'].'</TD><TD>'.$item['code'].'</TD><TD>'.$item['continent'].'</TD></TR>';
                        }
                        ?>
                    </TABLE>
                </div>
            </div>
        </div>
    </div>

文件位置:D:\phpwork\advanced\frontend\views\demo\form2a.php

<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
?>
    <div class="panel panel-warning">
        <div class="panel-heading">
            <div class="panel-title">
                二、Yii2的表单验证——服务器端验证的验证器(提交数据的显示)
            </div>
        </div>
        <div class="panel-body" style="height:500px;">
            <?php
            echo DetailView::widget([
                'model' => $model,
                'attributes' => [
                    'countryName',
                    'countryAbbr',
                    'continent',
                ],
                'template' => '<tr><th class="text-right" style="width:150px;">{label}</th><td>{value}</td></tr>',
            ]);
            ?>
            <button onclick="history.back()" class="btn btn-default">返 回</button>
        </div>
    </div>

Country类是Yii2官网实例中的一个类,请参照官网文档自行构建。
本例涉及的数据库表country,比官网实例中多了一个字段:continent,手动加上亦可:

CREATE TABLE `country` (
  `code` CHAR(2) NOT NULL PRIMARY KEY,
  `name` CHAR(52) NOT NULL,
  `continent` CHAR(52) NOT NULL,
  `population` INT(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `country` VALUES ('AU','Australia',24016400,'Oceania');
INSERT INTO `country` VALUES ('BR','Brazil',205722000,'South America');
INSERT INTO `country` VALUES ('CA','Canada',35985751,'North America');
INSERT INTO `country` VALUES ('CN','China',1375210000,'Asia');
INSERT INTO `country` VALUES ('DE','Germany',81459000,'Europe');
INSERT INTO `country` VALUES ('FR','France',64513242,'Europe');
INSERT INTO `country` VALUES ('GB','United Kingdom',65097000,'Europe');
INSERT INTO `country` VALUES ('IN','India',1285400000,'Asia');
INSERT INTO `country` VALUES ('RU','Russia',146519759,'Europe');
INSERT INTO `country` VALUES ('US','United States',322976000,'North America');

欢迎深入阅读:
一处编写,三处运行的Yii2表单验证

(全文完)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容