高級模型提供了更多的查詢功能和模型增強(qiáng)功能,利用了模型類的擴(kuò)展機(jī)制實現(xiàn)。如果需要使用高級模型的下面這些功能,記得需要繼承Think\Model\AdvModel類或者采用動態(tài)模型。
~~~
namespace Home\Model;
use Think\Model\AdvModel;
class UserModel extends AdvModel{
}
~~~
我們下面的示例都假設(shè)UserModel類繼承自Think\Model\AdvModel類。
## 字段過濾
基礎(chǔ)模型類內(nèi)置有數(shù)據(jù)自動完成功能,可以對字段進(jìn)行過濾,但是必須通過Create方法調(diào)用才能生效。高級模型類的字段過濾功能卻可以不受create方法的調(diào)用限制,可以在模型里面定義各個字段的過濾機(jī)制,包括寫入過濾和讀取過濾。
字段過濾的設(shè)置方式只需要在Model類里面添加 `$_filter`屬性,并且加入過濾因子,格式如下:
~~~
protected $_filter = array(
'過濾的字段'=>array('寫入過濾規(guī)則','讀取過濾規(guī)則',是否傳入整個數(shù)據(jù)對象),
)
~~~
過濾的規(guī)則是一個函數(shù),如果設(shè)置傳入整個數(shù)據(jù)對象,那么函數(shù)的參數(shù)就是整個數(shù)據(jù)對象,默認(rèn)是傳入數(shù)據(jù)對象中該字段的值。
舉例說明,例如我們需要在發(fā)表文章的時候?qū)ξ恼聝?nèi)容進(jìn)行安全過濾,并且希望在讀取的時候進(jìn)行截取前面255個字符,那么可以設(shè)置:
~~~
protected $_filter = array(
'content'=>array('contentWriteFilter','contentReadFilter'),
)
~~~
其中,contentWriteFilter是自定義的對字符串進(jìn)行安全過濾的函數(shù),而contentReadFilter是自定義的一個對內(nèi)容進(jìn)行截取的函數(shù)。通常我們可以在項目的公共函數(shù)文件里面定義這些函數(shù)。
## 序列化字段
序列化字段是新版推出的新功能,可以用簡單的數(shù)據(jù)表字段完成復(fù)雜的表單數(shù)據(jù)存儲,尤其是動態(tài)的表單數(shù)據(jù)字段。 要使用序列化字段的功能,只需要在模型中定義serializeField屬性,定義格式如下:
~~~
protected $serializeField = array(
'info' => array('name', 'email', 'address'),
);
~~~
Info是數(shù)據(jù)表中的實際存在的字段,保存到其中的值是name、email和address三個表單字段的序列化結(jié)果。序列化字段功能可以在數(shù)據(jù)寫入的時候進(jìn)行自動序列化,并且在讀出數(shù)據(jù)表的時候自動反序列化,這一切都無需手動進(jìn)行。
下面還是是User數(shù)據(jù)表為例,假設(shè)其中并不存在name、email和address字段,但是設(shè)計了一個文本類型的info字段,那么下面的代碼是可行的:
~~~
$User = D("User"); // 實例化User對象
// 然后直接給數(shù)據(jù)對象賦值
$User->name = 'ThinkPHP';
$User->email = 'ThinkPHP@gmail.com';
$User->address = '上海徐匯區(qū)';
// 把數(shù)據(jù)對象添加到數(shù)據(jù)庫 name email和address會自動序列化后保存到info字段
$User->add();
查詢用戶數(shù)據(jù)信息
$User->find(8);
// 查詢結(jié)果會自動把info字段的值反序列化后生成name、email和address屬性
// 輸出序列化字段
echo $User->name;
echo $User->email;
echo $User->address;
~~~
## 文本字段
ThinkPHP支持?jǐn)?shù)據(jù)模型中的個別字段采用文本方式存儲,這些字段就稱為文本字段,通??梢杂糜谀承㏕ext或者Blob字段,或者是經(jīng)常更新的數(shù)據(jù)表字段。
要使用文本字段非常簡單,只要在模型里面定義blobFields屬性就行了。例如,我們需要對Blog模型的content字段使用文本字段,那么就可以使用下面的定義:
~~~
Protected $blobFields = array('content');
~~~
系統(tǒng)在查詢和寫入數(shù)據(jù)庫的時候會自動檢測文本字段,并且支持多個字段的定義。
> 需要注意的是:對于定義的文本字段并不需要數(shù)據(jù)庫有對應(yīng)的字段,完全是另外的。而且,暫時不支持對文本字段的搜索功能。
## 只讀字段
只讀字段用來保護(hù)某些特殊的字段值不被更改,這個字段的值一旦寫入,就無法更改。 要使用只讀字段的功能,我們只需要在模型中定義readonlyField屬性
~~~
protected $readonlyField = array('name', 'email');
~~~
例如,上面定義了當(dāng)前模型的name和email字段為只讀字段,不允許被更改。也就是說當(dāng)執(zhí)行save方法之前會自動過濾到只讀字段的值,避免更新到數(shù)據(jù)庫。
下面舉個例子說明下:
~~~
$User = D("User"); // 實例化User對象
$User->find(8);
// 更改某些字段的值
$User->name = 'TOPThink';
$User->email = 'Topthink@gmail.com';
$User->address = '上海靜安區(qū)';
// 保存更改后的用戶數(shù)據(jù)
$User->save();
~~~
事實上,由于我們對name和email字段設(shè)置了只讀,因此只有address字段的值被更新了,而name和email的值仍然還是更新之前的值。
## 悲觀鎖和樂觀鎖
業(yè)務(wù)邏輯的實現(xiàn)過程中,往往需要保證數(shù)據(jù)訪問的排他性。如在金融系統(tǒng)的日終結(jié)算處理中,我們希望針對某個時間點的數(shù)據(jù)進(jìn)行處理,而不希望在結(jié)算進(jìn)行過程中(可能是幾秒種,也可能是幾個小時),數(shù)據(jù)再發(fā)生變化。此時,我們就需要通過一些機(jī)制來保證這些數(shù)據(jù)在某個操作過程中不會被外界修改,這樣的機(jī)制,在這里,也就是所謂的 “ 鎖 ” ,即給我們選定的目標(biāo)數(shù)據(jù)上鎖,使其無法被其他程序修改。 ThinkPHP支持兩種鎖機(jī)制:即通常所說的 “ 悲觀鎖( Pessimistic Locking ) ”和 “ 樂觀鎖( Optimistic Locking ) ” 。
### 悲觀鎖( Pessimistic Locking )
悲觀鎖,正如其名,它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度,因此,在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機(jī)制(也只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在本系統(tǒng)中實現(xiàn)了加鎖機(jī)制,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))。 通常是使用for update子句來實現(xiàn)悲觀鎖機(jī)制。
ThinkPHP支持悲觀鎖機(jī)制,默認(rèn)情況下,是關(guān)閉悲觀鎖功能的,要在查詢和更新的時候啟用悲觀鎖功能,可以通過使用之前提到的查詢鎖定方法,例如:
~~~
$User->lock(true)->save($data);// 使用悲觀鎖功能
~~~
### 樂觀鎖( Optimistic Locking )
相對悲觀鎖而言,樂觀鎖機(jī)制采取了更加寬松的加鎖機(jī)制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機(jī)制實現(xiàn),以保證操作最大程度的獨占性。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對長事務(wù)而言,這樣的開銷往往無法承受。 如一個金融系統(tǒng),當(dāng)某個操作員讀取用戶的數(shù)據(jù),并在讀出的用戶數(shù)據(jù)的基礎(chǔ)上進(jìn)行修改時(如更改用戶帳戶余額),如果采用悲觀鎖機(jī)制,也就意味著整個操作過程中(從操作員讀出數(shù)據(jù)、開始修改直至提交修改結(jié)果的全過程,甚至還包括操作員中途去煮咖啡的時間),數(shù)據(jù)庫記錄始終處于加鎖狀態(tài),可以想見,如果面對幾百上千個并發(fā),這樣的情況將導(dǎo)致怎樣的后果。樂觀鎖機(jī)制在一定程度上解決了這個問題。樂觀鎖,大多是基于數(shù)據(jù)版本( Version )記錄機(jī)制實現(xiàn)。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個版本標(biāo)識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表增加一個 “version” 字段來實現(xiàn)。
ThinkPHP也可以支持樂觀鎖機(jī)制,要啟用樂觀鎖,只需要繼承高級模型類并定義模型的optimLock屬性,并且在數(shù)據(jù)表字段里面增加相應(yīng)的字段就可以自動啟用樂觀鎖機(jī)制了。默認(rèn)的optimLock屬性是lock_version,也就是說如果要在User表里面啟用樂觀鎖機(jī)制,只需要在User表里面增加lock_version字段,如果有已經(jīng)存在的其它字段作為樂觀鎖用途,可以修改模型類的optimLock屬性即可。如果存在optimLock屬性對應(yīng)的字段,但是需要臨時關(guān)閉樂觀鎖機(jī)制,把optimLock屬性設(shè)置為false就可以了。
## 數(shù)據(jù)分表
對于大數(shù)據(jù)量的應(yīng)用,經(jīng)常會對數(shù)據(jù)進(jìn)行分表,有些情況是可以利用數(shù)據(jù)庫的分區(qū)功能,但并不是所有的數(shù)據(jù)庫或者版本都支持,因此我們可以利用ThinkPHP內(nèi)置的數(shù)據(jù)分表功能來實現(xiàn)。幫助我們更方便的進(jìn)行數(shù)據(jù)的分表和讀取操作。
和數(shù)據(jù)庫分區(qū)功能不同,內(nèi)置的數(shù)據(jù)分表功能需要根據(jù)分表規(guī)則手動創(chuàng)建相應(yīng)的數(shù)據(jù)表。
在需要分表的模型中定義partition屬性即可。
~~~
protected $partition = array(
'field' => 'name',// 要分表的字段 通常數(shù)據(jù)會根據(jù)某個字段的值按照規(guī)則進(jìn)行分表
'type' => 'md5',// 分表的規(guī)則 包括id year mod md5 函數(shù) 和首字母
'expr' => 'name',// 分表輔助表達(dá)式 可選 配合不同的分表規(guī)則
'num' => 'name',// 分表的數(shù)目 可選 實際分表的數(shù)量
);
~~~
定義好了分表屬性后,我們就可以來進(jìn)行CURD操作了,唯一不同的是,獲取當(dāng)前的數(shù)據(jù)表不再使用getTableName方法,而是使用getPartitionTableName方法,而且必須傳入當(dāng)前的數(shù)據(jù)。然后根據(jù)數(shù)據(jù)分析應(yīng)該實際操作哪個數(shù)據(jù)表。因此,分表的字段值必須存在于傳入的數(shù)據(jù)中,否則會進(jìn)行聯(lián)合查詢。
## 返回類型
系統(tǒng)默認(rèn)的數(shù)據(jù)庫查詢返回的是數(shù)組,我們可以給單個數(shù)據(jù)設(shè)置返回類型,以滿足特殊情況的需要,例如:
~~~
$User = M("User"); // 實例化User對象
// 返回結(jié)果是一個數(shù)組數(shù)據(jù)
$data = $User->find(6);
// 返回結(jié)果是一個stdClass對象
$data = $User->returnResult($data, "object");
// 還可以返回自定義的類
$data = $User->returnResult($data, "User");
~~~
返回自定義的User類,類的架構(gòu)方法的參數(shù)是傳入的數(shù)據(jù)。例如:
~~~
Class User {
public function __construct($data){
// 對$data數(shù)據(jù)進(jìn)行處理
}
}
~~~
- 序言
- 基礎(chǔ)
- 獲取ThinkPHP
- 環(huán)境要求
- 目錄結(jié)構(gòu)
- 入口文件
- 自動生成
- 模塊
- 控制器
- 開發(fā)規(guī)范
- 配置
- 配置格式
- 配置加載
- 讀取配置
- 動態(tài)配置
- 擴(kuò)展配置
- 批量配置
- 架構(gòu)
- 模塊化設(shè)計
- URL模式
- 多層MVC
- CBD模式
- 命名空間
- 自動加載
- 應(yīng)用模式
- 項目編譯
- 系統(tǒng)流程
- 路由
- 路由定義
- 規(guī)則路由
- 正則路由
- 靜態(tài)路由
- 閉包支持
- 實例說明
- 控制器
- 控制器定義
- 前置和后置操作
- Action參數(shù)綁定
- 偽靜態(tài)
- URL大小寫
- URL生成
- AJAX返回
- 跳轉(zhuǎn)和重定向
- 輸入變量
- 請求類型
- 空操作
- 空控制器
- 插件控制器
- 操作綁定到類
- 模型
- 模型定義
- 模型實例化
- 字段定義
- 連接數(shù)據(jù)庫
- 切換數(shù)據(jù)庫
- 分布式數(shù)據(jù)庫支持
- 連貫操作
- WHERE
- TABLE
- ALIAS
- DATA
- FIELD
- ORDER
- LIMIT
- PAGE
- GROUP
- HAVING
- JOIN
- UNION
- DISTINCT
- LOCK
- CACHE
- COMMENT
- RELATION
- USING
- fetchSql
- TOKEN
- STRICT
- INDEX
- 命名范圍
- CURD操作
- 數(shù)據(jù)創(chuàng)建
- 數(shù)據(jù)寫入
- 數(shù)據(jù)讀取
- 數(shù)據(jù)更新
- 數(shù)據(jù)刪除
- ActiveRecord
- 字段映射
- 查詢語言
- 查詢方式
- 表達(dá)式查詢
- 快捷查詢
- 區(qū)間查詢
- 組合查詢
- 統(tǒng)計查詢
- SQL查詢
- 動態(tài)查詢
- 子查詢
- 自動驗證
- 自動完成
- 參數(shù)綁定
- 虛擬模型
- 模型分層
- 視圖模型
- 關(guān)聯(lián)模型
- 高級模型
- Mongo模型
- 視圖
- 模板定義
- 模板主題
- 模板賦值
- 模板渲染
- 獲取模板地址
- 獲取內(nèi)容
- 模板引擎
- 模板
- 變量輸出
- 系統(tǒng)變量
- 使用函數(shù)
- 默認(rèn)值輸出
- 使用運(yùn)算符
- 標(biāo)簽庫
- 模板繼承
- 修改定界符
- 三元運(yùn)算
- 包含文件
- 內(nèi)置標(biāo)簽
- Volist標(biāo)簽
- Foreach標(biāo)簽
- For標(biāo)簽
- Switch標(biāo)簽
- 比較標(biāo)簽
- 范圍判斷標(biāo)簽
- IF標(biāo)簽
- Present標(biāo)簽
- Empty標(biāo)簽
- Defined標(biāo)簽
- Assign標(biāo)簽
- Define標(biāo)簽
- 標(biāo)簽嵌套
- import標(biāo)簽
- 使用PHP代碼
- 原樣輸出
- 模板注釋
- 模板布局
- 模板替換
- 調(diào)試
- 調(diào)試模式
- 異常處理
- 日志記錄
- 頁面Trace
- Trace方法
- 變量調(diào)試
- 性能調(diào)試
- 錯誤調(diào)試
- 模型調(diào)試
- 緩存
- 數(shù)據(jù)緩存
- 快速緩存
- 查詢緩存
- 靜態(tài)緩存
- 安全
- 輸入過濾
- 表單合法性檢測
- 表單令牌
- 防止SQL注入
- 目錄安全文件
- 保護(hù)模板文件
- 上傳安全
- 防止XSS攻擊
- 其他安全建議
- 擴(kuò)展
- 類庫擴(kuò)展
- 驅(qū)動擴(kuò)展
- 緩存驅(qū)動
- 數(shù)據(jù)庫驅(qū)動
- 日志驅(qū)動
- Session驅(qū)動
- 存儲驅(qū)動
- 模板引擎驅(qū)動
- 標(biāo)簽庫驅(qū)動
- 行為擴(kuò)展
- 標(biāo)簽擴(kuò)展
- Widget擴(kuò)展
- 應(yīng)用模式
- 部署
- PATH_INFO支持
- URL重寫
- 模塊部署
- 域名部署
- 入口綁定
- 替換入口
- 專題
- SESSION支持
- Cookie支持
- 多語言支持
- 數(shù)據(jù)分頁
- 文件上傳
- 驗證碼
- 圖像處理
- RESTFul
- RPC
- SAE
- IP獲取和定位
- 附錄
- 常量參考
- 配置參考
- 升級指導(dǎo)
- 鳴謝
