### 2018 年 11 月 2 日 發(fā)布
模型三大利器我們已經(jīng)了解了[修改器](https://blog.thinkphp.cn/817548)和[搜索器](https://blog.thinkphp.cn/783775),如果還不是特別理解的可以參考之前的博客。
今天來(lái)總結(jié)下獲取器的用法,其實(shí)獲取器是最容易理解的,但卻又是最容易困惑和出問(wèn)題的。
## 定義獲取器
獲取器的作用是對(duì)模型對(duì)象的(原始)數(shù)據(jù)做出自動(dòng)處理。一個(gè)獲取器對(duì)應(yīng)模型的一個(gè)特殊方法(該方法必須為`public`類型),方法命名規(guī)范為:
>[info]### get**FieldName**Attr
`FieldName`為數(shù)據(jù)表字段的駝峰轉(zhuǎn)換或者你數(shù)據(jù)表不存在的字段(注意理解后面這句話),下面是一個(gè)典型的獲取器定義:
```
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
public function getUserTypeAttr($value, $data)
{
$type = [0 => '普通', 1 => 'VIP', 2 => '黃金', 3 => '白金', 4 => '鉆石'];
return $type[$value];
}
}
```
你需要給每一個(gè)需要輸出轉(zhuǎn)換處理的數(shù)據(jù)字段定義一個(gè)對(duì)應(yīng)的獲取器,但獲取器的字段名不一定要和數(shù)據(jù)表的字段名一致,例如我希望給`user_type`字段定義一個(gè)名為`getTypeAttr`的獲取器也是允許的,但要注意這個(gè)時(shí)候傳入獲取器的第一個(gè)參數(shù)肯定是沒(méi)有值(因?yàn)闆](méi)有對(duì)應(yīng)的數(shù)據(jù)表字段數(shù)據(jù)),只能通過(guò)第二個(gè)參數(shù)獲取你需要的數(shù)據(jù)。
```
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
public function getTypeAttr($value, $data)
{
$type = [0 => '普通', 1 => 'VIP', 2 => '黃金', 3 => '白金', 4 => '鉆石'];
return $type[$data['user_type']];
}
}
```
當(dāng)然更為嚴(yán)謹(jǐn)?shù)那闆r下,你還需要判斷下是否存在`$data['user_type']`,這個(gè)暫且略過(guò)。
>[danger] 注意第二個(gè)參數(shù)的`data`數(shù)據(jù),可能本身已經(jīng)經(jīng)過(guò)了獲取器的處理(如果你定義了相關(guān)的獲取器的話)。
為什么要定義一個(gè)和數(shù)據(jù)報(bào)字段不一致的獲取器呢?最明顯的好處可以區(qū)分不同的字段獲取原始數(shù)據(jù)和處理過(guò)的數(shù)據(jù)。事實(shí)上,有很多理由可以讓你定義一些數(shù)據(jù)表不存在的字段獲取器,這恰恰是獲取器的魅力所在。
看的出來(lái)獲取器定義本身沒(méi)什么難度,關(guān)鍵在于方法里面的獲取邏輯,這是實(shí)際應(yīng)用中最需要關(guān)注的。
## 調(diào)用獲取器
定義獲取器之后會(huì)在下列情況自動(dòng)觸發(fā):
* 模型的數(shù)據(jù)對(duì)象取值操作(例如`$model->field_name`);
* 模型的序列化輸出操作(例如`$model->toArray()`或`toJson()`);
* 顯式調(diào)用`getAttr`方法(例如`$model->getAttr('field_name')`);
前面兩種其實(shí)最終都是調(diào)用最后一種來(lái)實(shí)現(xiàn)的,最關(guān)鍵的是要理解第一種。模型對(duì)象取值的時(shí)候一般都是通過(guò)下面的方式:
```
$user = User::get(1);
echo $user->name;
echo $user->user_type;
```
當(dāng)我們使用上面的方式進(jìn)行模型對(duì)象數(shù)據(jù)獲取或者在模板輸出的時(shí)候事實(shí)上都會(huì)按照下面的順序來(lái)檢測(cè)和獲取數(shù)據(jù)。
* 第`1`步——如果查詢結(jié)果包含該字段數(shù)據(jù),取回原始數(shù)據(jù),否則并進(jìn)入第2步;
* 第`2`步——檢查是否定義該字段的獲取器(包括動(dòng)態(tài)獲取器),如果有,則調(diào)用獲取器返回結(jié)果,沒(méi)有則進(jìn)入第3步;
* 第`3`步——檢查是否定義了字段的類型轉(zhuǎn)換,有則進(jìn)行轉(zhuǎn)換處理并返回結(jié)果,沒(méi)有則進(jìn)入第4步;
* 第`4`步——如果是系統(tǒng)的時(shí)間字段,則自動(dòng)進(jìn)行時(shí)間格式化處理并返回結(jié)果,否則進(jìn)入第5步;
* 第`5`步——如果第1步檢查的時(shí)候不包含該字段數(shù)據(jù),則檢查是否存在關(guān)聯(lián)屬性定義,有則通過(guò)關(guān)聯(lián)關(guān)系獲取數(shù)據(jù)并返回結(jié)果,否則拋出屬性未定義的異常。
>[info] 上面的這五個(gè)步驟的詳細(xì)代碼,如果你有興趣的可以直接參考`think\model\concern\Attribute`的`getAttr`方法代碼。
簡(jiǎn)單來(lái)說(shuō),當(dāng)你獲取`$user->user_type`的時(shí)候都會(huì)去檢查是否定義了相關(guān)的獲取器,而不管`user_type`字段是否是一個(gè)真實(shí)的數(shù)據(jù)表字段。
但很多情況下,你不會(huì)一個(gè)個(gè)去獲取模型數(shù)據(jù),而是把整個(gè)模型數(shù)據(jù)返回給客戶端或者模板。
```
public function index()
{
$user = User::get(1);
return json($user);
}
```
在這種情況下,其實(shí)就是在響應(yīng)輸出的時(shí)候進(jìn)行了模型的`toJson`處理。
>[danger] 有一點(diǎn)至關(guān)重要,如果你的獲取器定義了非數(shù)據(jù)表的字段,是不會(huì)自動(dòng)輸出的,必須通過(guò)`append`方法追加額外屬性(并且支持追加關(guān)聯(lián)模型屬性)。
如果我們定義了一個(gè)`type`屬性的獲取器(假設(shè)這并不是一個(gè)真實(shí)的數(shù)據(jù)表字段),那么需要使用下?的方式才能正常輸出(否則你可能只有`user_type`數(shù)據(jù)):
```
public function index()
{
$user = User::get(1);
return json($user->append(['type']));
}
```
如果你是使用`toArray`的話,處理方式相同。
如果是數(shù)據(jù)集查詢的話,一樣可以使用`append`方法統(tǒng)一追加額外字段。
```
public function index()
{
$users = User::all();
return json($users->append(['type']));
}
```
>[info] 除了`append`方法之外,我們還支持用`hidden`方法臨時(shí)隱藏一些數(shù)據(jù)。
## 獲取原始數(shù)據(jù)
有些情況下,除了要獲取處理過(guò)的數(shù)據(jù)外,還需要獲取原始數(shù)據(jù)以便應(yīng)對(duì)不同的需求。
如果?你的獲取器都是用的區(qū)分于實(shí)際數(shù)據(jù)表字段的額外屬性字段,那么這個(gè)問(wèn)題本身已經(jīng)解決了。所以我們主要討論的是當(dāng)你的獲取器屬性和數(shù)據(jù)表字段一致的情況下,該如何獲取原始數(shù)據(jù)。
一個(gè)最簡(jiǎn)單的辦法是使用`getData`方法:
```
$user = User::get(1);
// 獲取user_type獲取器數(shù)據(jù)
echo $user->user_type;
// 獲取原始的user_type數(shù)據(jù)
echo $user->getData('user_type');
// 獲取全部原始數(shù)據(jù)
dump($user->getData());
```
## 動(dòng)態(tài)獲取器
前面我們提到過(guò)動(dòng)態(tài)獲取器的概念,動(dòng)態(tài)獲取器就是不需要在模型類里面定義獲取器方法,而是在查詢的時(shí)候使用閉包來(lái)定義一個(gè)字段的獲取器對(duì)數(shù)據(jù)進(jìn)行統(tǒng)一的處理。
```
User::withAttr('name', function($value, $data) {
return strtolower($value);
})->select();
```
如果你需要定義多個(gè)動(dòng)態(tài)獲取器,多次調(diào)用`withAttr`方法就行。
動(dòng)態(tài)獲取器的意義除了可以不用在模型里面定義獲取器方法之外,還可以起到覆蓋已經(jīng)定義的獲取器的作用,并且動(dòng)態(tài)獲取器可以支持`Db`類操作,彌補(bǔ)了`Db`操作不能使用獲取器的缺憾,具體就看自己的需求來(lái)選擇了。
```
Db::name('user')->withAttr('name', function($value, $data) {
return strtolower($value);
})->select();
```
## 總結(jié)
無(wú)論是獲取器,還是之前提?的修改器、搜索器,其作用無(wú)非是把你的模型工作細(xì)化和拆分,這樣代碼和邏輯也會(huì)更清晰,可維護(hù)性也大大增強(qiáng),至于性能,從來(lái)不是模型首先考慮的。
- 值得升級(jí)到5.1的18個(gè)理由
- 5.1.7版本新特性
- JSON字段類型在ORM中的使用
- 文件下載響應(yīng)對(duì)象
- 教你使用5.1的數(shù)組對(duì)象查詢
- 模型三大利器之一:搜索器
- 在ThinkPHP中使用Yaconf
- 掌握命令行的表格輸出
- 5.1.25查詢參數(shù)綁定的改進(jìn)
- ThinkPHP安全規(guī)范指引
- 巧用數(shù)據(jù)集的排序功能實(shí)現(xiàn)統(tǒng)計(jì)排序
- think-orm ——基于5.1的獨(dú)立ORM庫(kù)
- think-template——基于ThinkPHP的獨(dú)立模板引擎
- ThinkPHP5.1.26版本發(fā)布——修正版本,包含安全更新
- ThinkPHP5.0和3.2再發(fā)安全更新
- 官宣:ThinkPHP發(fā)布首個(gè)LTS版本
- 你真的了解Db類和模型的正確使用姿勢(shì)么?
- 如何更有效的記錄和管理日志
- 模型三大利器之二:修改器
- ThinkPHP5.1.28版本發(fā)布——修正上一版本問(wèn)題,改進(jìn)關(guān)聯(lián)查詢
- 模型三大利器之三:獲取器
- API版本控制的幾種思路
- ThinkPHP5.2第一個(gè)Beta版本發(fā)布測(cè)試
- 讓你少犯錯(cuò)的數(shù)據(jù)查詢基本原則
- ThinkPHP發(fā)布5.1.29版本——常規(guī)更新
- 這15個(gè)好習(xí)慣讓你更容易升級(jí)到5.2
- 如何有效提高ThinkPHP的應(yīng)用性能
- 讓你提高開(kāi)發(fā)效率的查詢技巧
- 模型關(guān)聯(lián)查詢不完全指南
- 5.2發(fā)布Beta2版本——統(tǒng)一和精簡(jiǎn)大量用法
- ThinkPHP發(fā)布5.1.30版本——支持微秒時(shí)間字段寫(xiě)入
- ThinkPHP的數(shù)據(jù)緩存使用
- ThinkPHP5.2安裝及入口文件
- ThinkPHP榮獲2018 年度最受歡迎中國(guó)開(kāi)源開(kāi)發(fā)框架第1名
- 5.1路由使用心得技巧
- ThinkPHP5.*版本發(fā)布安全更新
- ThinkPHP項(xiàng)目及代碼規(guī)范指北
- 5.2版本的設(shè)計(jì)規(guī)范指導(dǎo)
- ThinkPHP5.1.32版本發(fā)布——圣誕快樂(lè)
- 利用Trait特性給模型增加樂(lè)觀鎖功能
- 5.2數(shù)據(jù)庫(kù)和模型的變化(摘要)
- ThinkPHP模板引擎實(shí)現(xiàn)和常見(jiàn)問(wèn)題
- ThinkPHP5.0.24版本發(fā)布——安全更新
- 不忘初心,方得始終——ThinkPHP十三周年報(bào)告
- ThinkPHP5+相關(guān)資源匯總
- 異步社區(qū)ThinkPHP周年慶專享優(yōu)惠活動(dòng)
- 5.2路由的調(diào)整和改進(jìn)
- ThinkPHP發(fā)布5.1.33版本——包含安全更新
- ThinkPHP擴(kuò)展開(kāi)發(fā)指南
- ThinkPHP發(fā)布5.2Beta3版本
- ThinkPHP發(fā)布5.1.34版本——喜迎新年
- ThinkPHP發(fā)布5.2RC1版本
- ThinkPHP發(fā)布5.1.35版本——常規(guī)更新
- 5.2配置類的調(diào)整
- 5.2時(shí)間查詢的改進(jìn)和優(yōu)化
- 5.2RC版本升級(jí)不完全指導(dǎo)(僅供學(xué)習(xí)參考)
- ThinkPHP5.2版本正式變更為6.0版本
- ThinkPHP百度云云虛擬主機(jī)專享免費(fèi)活動(dòng)
- 事件系統(tǒng)以及查詢事件、模型事件的使用
- ThinkPHP6.0RC2版本發(fā)布——架構(gòu)升級(jí)、精簡(jiǎn)核心
- ThinkPHP5.1.36LTS版本發(fā)布——常規(guī)更新
- 新版Session和Cookie設(shè)計(jì)變化
- ThinkPHP5.1.37版本發(fā)布——常規(guī)更新
- ThinkPHP6.0RC3版本發(fā)布——細(xì)節(jié)完善,體驗(yàn)優(yōu)化
- 6.0中間件使用詳解
- Composer各大廠商鏡像地址
- ThinkPHP6.0發(fā)布計(jì)劃公告
- 「ThinkPHP開(kāi)發(fā)者周刊」招募志愿者
- ThinkPHP6.0日志變化
- ThinkPHP5.1.38版本發(fā)布——常規(guī)更新
- ThinkPHP6.0RC4版本發(fā)布——ORM獨(dú)立,日志多通道支持
- ThinkORM2.0開(kāi)發(fā)指南上線
- ThinkPHP6.0RC5版本發(fā)布——多應(yīng)用模式獨(dú)立,中間件機(jī)制調(diào)整
- ThinkPHP6.0版本發(fā)布——程序員節(jié)福利
- ThinkPHP5.1.39LTS版本發(fā)布——常規(guī)更新
- ThinkPHP6.0.1版本發(fā)布——圣誕快樂(lè)!
- 回顧2019,展望2020!
- ThinkPHPV6.0.2版本發(fā)布——2020新春快樂(lè)!
- 周年福利系列:Swoole合作優(yōu)惠
- 億速云成為ThinkPHPV6.0獨(dú)家贊助發(fā)布商??
- 新冠疫情工具和限免資源專題(保持更新中)
- 周年福利系列:創(chuàng)宇信用認(rèn)證合作優(yōu)惠
- 周年福利系列:碼云企業(yè)版限時(shí)10%優(yōu)惠
- 周年福利系列:想天短說(shuō)抵現(xiàn)優(yōu)惠
- think-swoole直播:從零開(kāi)始掌握swoole開(kāi)發(fā)
- 周年福利系列:B2C開(kāi)源電商ShopXO授權(quán)8折優(yōu)惠
- 周年福利系列:LayuiAdmin 永久授權(quán)限時(shí)優(yōu)惠
- ThinkPHP資源導(dǎo)航站上線——構(gòu)建生態(tài) 服務(wù)未來(lái)
- ThinkPHP官方技術(shù)支持服務(wù)和應(yīng)用服務(wù)市場(chǎng)上線公測(cè)
- ThinkPHP市場(chǎng)精選——推廣基本要素
- ThinkPHP市場(chǎng)精選——客服聊天專題
- ThinkPHPV6.0.3版本發(fā)布——端午安康
- ThinkPHP開(kāi)發(fā)者扶持計(jì)劃
- 6.0.3版本關(guān)鍵更新及升級(jí)事項(xiàng)
- 「ThinkPHP開(kāi)發(fā)者周刊」改版重啟
- ThinkPHP市場(chǎng)精選——企業(yè)建站專題
- ThinkPHP 提供統(tǒng)一API接口服務(wù)
- ThinkPHP市場(chǎng)精選——直播電商專題
- ThinkAPI服務(wù)SDK發(fā)布
- 官方服務(wù)市場(chǎng)啟用獨(dú)立子域名
- ThinkPHP市場(chǎng)精選——刷臉支付專題
- ThinkAPI推出會(huì)員服務(wù)計(jì)劃
- ThinkPHPV6.0.4版本發(fā)布——中秋國(guó)慶雙節(jié)快樂(lè)
- ThinkPHPV5.1.40版本發(fā)布——常規(guī)更新
- 1024程序員節(jié)福利走一波
- ThinkPHP V6.0.5版本發(fā)布——兼容Composer2.0
- 知識(shí)圖譜應(yīng)用場(chǎng)景——源論技術(shù)沙龍
- ThinkPHP5.*版本改進(jìn)Composer2.0的兼容
- 官方市場(chǎng)雙十一精選推薦
- 技術(shù)人做產(chǎn)品有機(jī)會(huì)么(文末送課程)
- 本周秒殺——古德云售后獲客營(yíng)銷系統(tǒng)
- ThinkAPI服務(wù)更新——支持接口分組和PHP版本依賴調(diào)整
- PHP8新特性盤點(diǎn)
- PHP8新特性系列:構(gòu)造器屬性提升使用及注意事項(xiàng)
- ThinkPHP2021新年寄語(yǔ)
- ThinkPHP V6.0.6&V5.1.41版本發(fā)布——兼容PHP8.0
- PHP如何更優(yōu)雅地調(diào)用API接口
- ThinkPHP V6.0.7發(fā)布——修正版本
- ThinkAPI服務(wù)更新——IP白名單
- 最新版ThinkORM對(duì)于時(shí)間字段的調(diào)整
- ThinkAPI短信接口正式上線
- ThinkPHP V6.0.8版本發(fā)布——多環(huán)境變量配置支持
- 頂想云寫(xiě)作服務(wù)開(kāi)啟第一次公測(cè)
- ThinkSSL上線——官方SSL/TLS證書(shū)服務(wù)
- MDBootstrap國(guó)內(nèi)用戶福利——ThinkPHP官方市場(chǎng)首發(fā)
- ThinkPHP V6.0.9版本發(fā)布——常規(guī)更新
- ThinkORM功能盤點(diǎn)——虛擬模型
- 全面支持主流GIT版本庫(kù)——云寫(xiě)作服務(wù)第二次公測(cè)
- 云寫(xiě)作服務(wù)私有化部署方案之:版本庫(kù)私有化
- 看云雙十一活動(dòng)
- ThinkPHP V6.0.10LTS發(fā)布——兼容PHP8.1
- ThinkPHP V6.0.12發(fā)布——命令行兼容8.1
- 頂想云知識(shí)管理上線公測(cè)——構(gòu)建企業(yè)文檔中心和知識(shí)庫(kù)
- 頂想云上線——助力生態(tài)數(shù)字化建設(shè)
- 618活動(dòng)進(jìn)行中——官方市場(chǎng)迎來(lái)一波更新
- 頂想云知識(shí)管理正式上線——看云文檔啟動(dòng)遷移服務(wù)
- ThinkPHP V6.0.13發(fā)布——常規(guī)更新
- 頂想云網(wǎng)站助理服務(wù)上線——構(gòu)建產(chǎn)品支持服務(wù)
- ThinkPHP發(fā)布6.1.0&6.0.14版本——安全更新
- ThinkPHP新版社區(qū)上線試運(yùn)營(yíng)
- ThinkAPI上架人臉核身接口——助力網(wǎng)站實(shí)名認(rèn)證
- 辭舊迎新——舊版社區(qū)停止注冊(cè)及發(fā)帖
- ThinkPHP6.1.2版本發(fā)布——兼容PHP8.2
