# 增加驗證
由于我們使用的是基于html5的瀏覽器,所以當我們把input的type值設置為email時,會對輸入的值是否為正確的郵箱進行驗證。如下圖所示:

但我們也應該非常清楚的認識到,并不是所有的用戶都在使用基于HTML5的瀏覽器。也就是說,僅僅靠前臺的驗證并不可靠。
**<span class="text-danger">實際的生產環境更是這樣:用戶不但可以使用非html5的瀏覽器,還可以使用一些其它的軟件來直接進行數據的POST操作。</span>**
> 前臺的驗證目的是為了提升用戶的使用感受; 后臺驗證的目的,是為了保證數據的合法性。兩者相輔相成,缺一不可。
## 暫時去除前臺的email驗證
我們將
~~~
<input type="email" name="email" />
~~~
修改為:
~~~
<input type="text" name="email" />
~~~
測試結果如下圖所示:


我們發現,非法的郵箱被傳入了。
# 開啟驗證
> 官方參考文檔:http://www.hmoore.net/manual/thinkphp5/129319 http://www.hmoore.net/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;
}
}
~~~
我們發現,增加自動驗證后,即使字段中全部有值,也會產生錯誤。
測試:

## 改寫框架
剛剛我們雖然實現了新增數據驗證的目的,但實現的并不完美。
我們更希望看到:
~~~
// 新增對象至數據表
$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();
~~~
測試:

~~~
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.hmoore.net/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.hmoore.net/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'));
}
...
}
~~~
測試結果如下圖所示:

下面,再給出幾組測試的示例代碼。
測試用戶名是否被占用:
~~~
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.hmoore.net/yunzhiclub/thinkphp5guide 點擊【試讀】按鈕右邊的【購買】按鈕進行購買,購買后可繼續學習后續章節的內容。
> 為便于大家交流,我們新近申請了QQ群,群號:338438158。我們期待在群中看到您的身影 ,并殷切地期望當其他的小伙伴遇到相同的問題時,您能伸出援助之手。
- 序言
- 第一章 準備知識
- 第一節:XAMPP
- 第二節:NAVICAT
- 第三節:GIT
- 第二章 HelloWorld
- 第一節:下載THINPHP5.0
- 第二節:啟動xampp
- 第三節:hello thinkphp
- 第四節:Hello world
- 第五節:MCA
- 第六節:單引號與雙引號
- 第三章 教師管理
- 第一節:新建數據庫
- 第二節:CURD之R 讀數據
- 3.2.1 時序圖
- 3.2.2 連接數據庫
- 3.2.3 直接讀取數據
- 3.2.4 間接讀取數據
- 3.2.5 使用V層顯示數據
- 3.2.6 引入bootstrap
- 3.2.7 加入bootstrap樣式
- 第三節:CURD之 C 增加數據
- 3.3.1 插入數據
- 3.3.2 表單傳值
- 3.3.3 存儲表單數據
- 3.3.4 自動時間戳
- 3.3.5 增加驗證
- 第四節:CURD之 D 刪除數據
- 3.4.1 什么是關鍵字
- 3.4.2 刪除數據
- 3.4.3 定制刪除鏈接
- 3.4.4 完善刪除
- 第五節:CURD之 U 更新數據
- 3.5.1 讀取數據
- 3.5.2 展示數據
- 3.5.3 修改數據
- 3.5.4 傳遞數據
- 3.5.5 接收數據
- 3.5.6 更新數據 方法一
- 3.5.7 更新數據 方法二
- 第六節:銜接各個action & 重構代碼
- 3.6.1 index action
- 3.6.2 add action
- 3.6.3 insert action
- 3.6.4 delete action
- 3.6.5 edit action
- 3.6.6 update action
- 第七節:分頁
- 3.7.1 調用內置分頁
- 3.7.2 select與paginate(選學)
- 3.7.3 分頁原理(選學)
- 3.7.4 調用page(選學)
- 第八節:模糊查詢
- 3.8.1 增加查詢
- 3.8.2 完善查詢
- 第四章 登錄與注銷
- 第一節:cookie 與 session
- 第二節:靜態方法
- 第三節:規劃URL跳轉
- 第四節:登錄
- 4.4.1 登錄流程
- 4.4.2 index action
- 4.4.3 login action
- 4.4.4 引入M層
- 4.4.5 異常Exception
- 第五節:注銷
- 第六節:驗證
- 4.6.1 action增加驗證
- 4.6.2 使用構造函數驗證
- 4.6.3 使用繼承驗證
- 第五章 E-R圖與數據字典
- 第一節:第一張E-R圖
- 第二節:實體間的關系
- 第三節:開發規范
- 第六章 班級管理
- 第一節:建立數據表
- 第二節:數據列表index
- 第三節:增加數據add
- 第四節:編輯數據edit
- 第五節:刪除數據delete
- 第七章 學生管理
- 第一節:建立數據表
- 第二節:數據列表
- 7.2.1 顯示性別
- 7.2.2 顯示創建時間
- 7.2.3 顯示班級名稱
- 7.2.4 顯示輔導員姓名
- 第三節:編輯數據
- 第四節:魔法函數
- 第五節:源碼分析
- 第八章 課程管理
- 第一節:建立數據表
- 第二節:新增課程
- 第三節:編輯課程
- 8.3.1 edit
- 8.3.2 update
- 第九章 代碼重構
- 第一節:add 與 edit
- 第二節:insert 與 update
- 第三節:模板繼承
- 9.3.1 index.html
- 9.3.2 edit.html
- 9.3.3 小結
- 第十章 UML圖
- 第一節:E-R圖回顧
- 第二節:UML圖
- 第十一章 菜單與路由
- 第一節:添加菜單
- 第二節:虛擬主機
- 第三節:定制路由
- 第十二章 開發調試
- 第一節:開啟trace
- 第二節:查看日志
- 第十三章:總結