# 增加验证 由于我们使用的是基于html5的浏览器,所以当我们把input的type值设置为email时,会对输入的值是否为正确的邮箱进行验证。如下图所示: ![](https://box.kancloud.cn/2016-06-14_575fa8c0379b2.png) 但我们也应该非常清楚的认识到,并不是所有的用户都在使用基于HTML5的浏览器。也就是说,仅仅靠前台的验证并不可靠。 **<span class="text-danger">实际的生产环境更是这样:用户不但可以使用非html5的浏览器,还可以使用一些其它的软件来直接进行数据的POST操作。</span>** > 前台的验证目的是为了提升用户的使用感受; 后台验证的目的,是为了保证数据的合法性。两者相辅相成,缺一不可。 ## 暂时去除前台的email验证 我们将 ~~~ <input type="email" name="email" /> ~~~ 修改为: ~~~ <input type="text" name="email" /> ~~~ 测试结果如下图所示: ![](https://box.kancloud.cn/2016-06-14_575fa8c04c022.png) ![](https://box.kancloud.cn/2016-06-14_575fa8c060f64.png) 我们发现,非法的邮箱被传入了。 # 开启验证 > 官方参考文档:http://www.kancloud.cn/manual/thinkphp5/129319 http://www.kancloud.cn/manual/thinkphp5/129355 使用验证以前,我们需要先用Teacher模型新建一个验证模型。 其实不用太纠结『模型』这个字眼,简单理解一下,模型就是一个类。之所以这样叫,我们现在仅仅是为了区分『控制器』这个名词,因为控制器也是一个类。很显然,虽然大家都是类,但是我们还是想把它们区分开。 增加如下验证文件: ~~~ <?php namespace app\common\validate; use think\Validate; // 内置验证类 class Teacher extends Validate { protected $rule = [ 'email' => 'email', ]; } ~~~ > 是的,文件的位置必须放在application\common\validate 下,并且,必须和我们的数据表模型名称一致。 因为只有这样,thinkphp这个框架才能找到它。 修改C层代码,加入验证信息: ~~~ public function insert() { // 接收传入数据 $postData = Request::instance()->post(); // 实例化Teacher空对象 $Teacher = new Teacher(); // 为对象赋值 $Teacher->name = $postData['name']; $Teacher->username = $postData['username']; $Teacher->sex = $postData['sex']; $Teacher->email = $postData['email']; // 新增对象至数据表 $result = $Teacher->validate(true)->save($Teacher->getData()); // 反馈结果 if (false === $result) { return '新增失败:' . $Teacher->getError(); } else { return '新增成功。新增ID为:' . $Teacher->id; } } ~~~ 我们发现,增加自动验证后,即使字段中全部有值,也会产生错误。 测试: ![](https://box.kancloud.cn/2016-06-14_575fa8c094b0c.png) ## 改写框架 刚刚我们虽然实现了新增数据验证的目的,但实现的并不完美。 我们更希望看到: ~~~ // 新增对象至数据表 $result = $Teacher->validate(true)->save(); ~~~ 而不是: ~~~ // 新增对象至数据表 $result = $Teacher->validate(true)->save($Teacher->getData()); ~~~ 原因很简单的,上面的写法才是真正的面象对象。正确的思想是:我们给一个对象赋值,然后对这个对象进行验证。显然,上面的写法更贴近我们的真实想法。 ThinkPHP是一个开源的软件,我们可以在Github中,找到它的身影 。下一个贡献者会不会就是你呢? 在这里,为了更好的实现验证,我们对`think\Model`及`think\Validate`类进行改写。 * * * * * > 如果你并不想动框架的源代码,那么可以略过下面本节中我们下面要讲的内容。只需要在以后涉及到验证操作时,使用`$Teacher->validate(true)->save($Teacher->getData());`即可。 * * * * * 一、`think\Model`类文件的位于`thinkphp/library/think/Model.php` 1、我们找到其中的save方法,改写前: ~~~ public function save($data = [], $where = [], $sequence = null) { if (!empty($data)) { // 数据自动验证 if (!$this->validateData($data)) { return false; } // 数据对象赋值 foreach ($data as $key => $value) { $this->setAttr($key, $value, $data); } if (!empty($where)) { $this->isUpdate = true; } } // 检测字段 if (!empty($this->field)) { $this->db(); foreach ($this->data as $key => $val) { if (!in_array($key, $this->field)) { unset($this->data[$key]); } } } ... ~~~ 改写后: ~~~ public function save($data = [], $where = [], $sequence = null) { if (!empty($data)) { // 数据自动验证 if (!$this->validateData($data)) { return false; } // 数据对象赋值 foreach ($data as $key => $value) { $this->setAttr($key, $value, $data); } if (!empty($where)) { $this->isUpdate = true; } // 未传入数据,则依新增与更新分别进行验证 --- 梦云智 } else { // 更新,只验证有变化的值 if ($this->isUpdate && !empty($this->change)) { foreach ($this->change as $value) { $data[$value] = $this->getData($value); } // 新增,验证全部的值 } else { $data = $this->getData(); } // 数据自动验证 if (!$this->validateData($data, $this->isUpdate)) { return false; } } // 检测字段 if (!empty($this->field)) { $this->db(); foreach ($this->data as $key => $val) { if (!in_array($key, $this->field)) { unset($this->data[$key]); } } } ... ~~~ 2、我们再找到该文件中的validateData方法 改写前: ~~~ /** * 自动验证数据 * @access protected * @param array $data 验证数据 * @return bool */ protected function validateData($data) ... $validate = Loader::validate($name); if (!empty($scene)) { $validate->scene($scene); } } if (!$validate->check($data)) { ... ~~~ 改写后: ~~~ /** * 自动验证数据 * @access protected * @param array $data 验证数据 * @param bool $isUpdate 是否为数据的更新操作 --- 梦云智 * @return bool */ protected function validateData($data, $isUpdate = false) ... $validate = Loader::validate($name); if (!empty($scene)) { $validate->scene($scene); } } // 进行数据更新操作则只验证有变化的字段 --- 梦云智 if ($isUpdate) { $validate->reMakeRule($data); } if (!$validate->check($data)) { ... ~~~ 二、 `think\Validate`类文件的位于`thinkphp/library/think/Validate.php` 在类中新增`reMakeRule`方法,比如我在`rule()方法`后后面进行添加。 ~~~ /** * 重新生成规则 (删除规则中不存在的更新字段规则 ) * @param array $datas 要验证的数据 * @return * @author panjie@yunzhiclub.com http://www.mengyunzhi.com * @DateTime 2016-10-21T13:13:44+0800 */ public function reMakeRule($datas = []) { $rule = []; if (is_array($datas)) { foreach ($datas as $key => $data) { if (array_key_exists($key, $this->rule)) { $rule[$key] = $this->rule[$key]; } } } $this->rule = $rule; } ~~~ 有了上述代码后,我们在C层就可以这样使用了。 ~~~ // 新增对象至数据表 $result = $Teacher->validate(true)->save(); ~~~ 测试: ![](https://box.kancloud.cn/2016-06-14_575fa8c094b0c.png) ~~~ git checkout -f step3.3.5.1 ~~~ 虽然只更改了几处代码,但仍然存在一定的难度。为此,我们在代码仓库中已经为大家改写好了上述两个文件。我们强烈建议你执行完`git checkout -f step3.3.5.1`后,将上述的两个文件复制到你的项目当中(由thinkphp5guide复制到thinkphp5中)。 如果你还没有安装git,那么可以点击如下链接,下载我们在百度网盘中为你准备好的文件。 https://pan.baidu.com/s/1kVHeIaj#list/path=%2FThinkphp5%E5%85%A5%E9%97%A8%E5%AE%9E%E4%BE%8B%E6%95%99%E7%A8%8B%2Fthinkphp%2Flibrary%2Fthink ## 实际项目的验证方法 在实际的项目开发中,我们不但需要对邮箱格式是否有误进行验证,还需要对用户名的最大最小长度,用户姓名的最大最小长度,传入的性别非0即1等,进行验证。 按上述要求,我们对验证规则进行改写: > 参考官方手册:http://www.kancloud.cn/manual/thinkphp5/129356 ~~~ <?php namespace app\common\validate; use think\Validate; // 内置验证类 class Teacher extends Validate { protected $rule = [ 'username' => 'require|unique:teacher|length:4,25', 'name' => 'require|length:2,25', 'sex' => 'in:0,1', 'email' => 'email', ]; } ~~~ 是的,我并没有去写错误提示信息,因为thinkphp已经给我们内置了错误提示信息。只有当我们认为thinkphp内置的错误提示信息不符合我们的要求时,我们才会去自已定义。 ## 再说测试 我们一直在强调测试的方法,上面的验证规则写完以后,我们当然首先想的就是继续利用form表单进行验证。但我想说的是,如果是那样的话,我们在更改验证规则时,刷新页面,重新添加数据,然后重新提交后查看验结果。显然,效率有一点低。 恰当的方法是我们新建一个专门用于做测试的方法,在实际的项目开发中,我们往往会专门的新建一个控制器专门用来做测试使用。 > 控制器验证 http://www.kancloud.cn/manual/thinkphp5/129354 通过查看官方手册我们得之,除了可以直接在模型中自动关联验证外,还可以在控制器中进行自动关联验证。在这里,我们采用在控制器中直接调用验证方法来进行测试。 示例代码: 验证username为空是否报错的示例代码: ~~~ ... namespace app\index\controller; class TeacherController extends Controller { ... public function test() { $data = array(); $data['username'] = ''; $data['name'] = '1'; $data['sex'] = '1'; $data['email'] = 'hello@hello.com'; var_dump($this->validate($data, 'Teacher')); } ... } ~~~ 测试结果如下图所示: ![](https://box.kancloud.cn/0d87ca78167e47d96bf0480855d01775_866x70.png) 下面,再给出几组测试的示例代码。 测试用户名是否被占用: ~~~ public function test() { $data = array(); $data['username'] = 'ceshi'; $data['name'] = '1'; $data['sex'] = '1'; $data['email'] = 'hello@hello.com'; var_dump($this->validate($data, 'Teacher')); } ~~~ 测试用户名是否过短: ~~~ public function test() { $data = array(); $data['username'] = 'ce'; $data['name'] = '1'; $data['sex'] = '1'; $data['email'] = 'hello@hello.com'; var_dump($this->validate($data, 'Teacher')); } ~~~ 其它测试信息,请自己尝试修改。 ### 还原V层 ~~~ <input type="email" name="email" /> ~~~ ~~~ git checkout -f step3.3.5.2 ~~~ **提醒:** **试读章节**到此为止,如果你还希望继续和我们一起学习,请打开网址:http://www.kancloud.cn/yunzhiclub/thinkphp5guide 点击【试读】按钮右边的【购买】按钮进行购买,购买后可继续学习后续章节的内容。 > 为便于大家交流,我们新近申请了QQ群,群号:338438158。我们期待在群中看到您的身影 ,并殷切地期望当其他的小伙伴遇到相同的问题时,您能伸出援助之手。