模型提供了比數(shù)據(jù)庫(kù)類更為強(qiáng)大的數(shù)據(jù)處理功能,本章講解了如何使用模型的各種數(shù)據(jù)自動(dòng)處理機(jī)制來(lái)簡(jiǎn)化開(kāi)發(fā),包括模型數(shù)據(jù)的轉(zhuǎn)換和輸出抬闷,是模型的核心和必須掌握的部分,學(xué)習(xí)內(nèi)容主要包含:
- 獲取器和修改器
- 自動(dòng)時(shí)間字段
- 數(shù)據(jù)類型轉(zhuǎn)換
- 數(shù)據(jù)自動(dòng)完成
- 數(shù)據(jù)轉(zhuǎn)換和輸出
- 模型事件
- 數(shù)據(jù)分批處理
- 總結(jié)
獲取器和修改器
獲取器和修改器是5.0
模型的核心功能之一刹淌,也是數(shù)據(jù)處理的關(guān)鍵饶氏,它們的配合完成了模型數(shù)據(jù)的輸入和輸出(自動(dòng))處理讥耗,但獲取器和修改器并非一一對(duì)應(yīng)。
獲取器
獲取器的作用是對(duì)模型的數(shù)據(jù)對(duì)象的(原始)數(shù)據(jù)做出自動(dòng)處理疹启。一個(gè)獲取器對(duì)應(yīng)模型的一個(gè)特殊方法古程,方法格式為:
getFieldNameAttr
FieldName
為數(shù)據(jù)表字段的駝峰轉(zhuǎn)換,定義了獲取器之后會(huì)在下列情況自動(dòng)觸發(fā):
- 模型的數(shù)據(jù)對(duì)象取值操作(
$model->field_name
)喊崖; - 模型的序列化輸出操作(
$model->toArray()
)挣磨; - 顯式調(diào)用
getAttr
方法($this->getAttr('field_name')
);
獲取器的場(chǎng)景包括:
- 時(shí)間日期字段的格式化輸出荤懂;
- 集合或枚舉類型的輸出茁裙;
- 數(shù)字狀態(tài)字段的輸出;
- 組合字段的輸出节仿;
例如晤锥,User模型有一個(gè)時(shí)間戳類型的birthday
屬性,那么如果使用
$user = User::get(1);
// 輸出 1234567890
echo $user->birthday;
輸出的是一個(gè)數(shù)字時(shí)間戳廊宪,并不是標(biāo)準(zhǔn)日期字符串格式矾瘾,一般的處理是,我們手動(dòng)處理箭启,例如:
$user = User::get(1);
// 輸出 2009-02-14
echo date('Y-m-d', $user->birthday);
這只是舉個(gè)例子壕翩,因?yàn)閿?shù)據(jù)的輸出處理可能非常的復(fù)雜,涉及到多個(gè)字段的不同處理方式傅寡。
定義模型的讀取器可以簡(jiǎn)化此類操作放妈,例如:
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected function getBirthdayAttr($value)
{
return date('Y-m-d', $value);
}
}
getBirthdayAttr
就是一個(gè)獲取器方法,定義后再次測(cè)試下面的示例荐操,結(jié)果就不同了芜抒。
$user = User::get(1);
// 輸出 2009-02-14
echo $user->birthday;
定義了獲取器之后,如果要獲取原始數(shù)據(jù)的值怎么辦呢托启?框架提供了getData
方法挽绩,用法如下:
$user = User::get(1);
// 輸出 2009-02-14
echo $user->birthday;
// 輸出 1234567890
echo $user->getData('birthday');
如果要獲取數(shù)據(jù)表的create_time
字段,獲取器方法定義方式為:
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected function getBirthdayAttr($value)
{
return date('Y-m-d', $value);
}
protected function getCreateTimeAttr($value)
{
return date('Y-m-d H:i:s', $value);
}
}
獲取create_time
字段值的時(shí)候驾中,使用:
$user = User::get(1);
// 輸出 2009-02-14
echo $user->create_time;
// 或者
echo $user->createTime;
獲取模型的對(duì)象屬性的時(shí)候駝峰法和小寫命名方式都可以取到值。
如果你的獲取器方法需要根據(jù)其它字段的值來(lái)組合模聋,可以給獲取器方法添加第二個(gè)參數(shù)肩民,如下:
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected function getBirthdayAttr($value)
{
return date('Y-m-d', $value);
}
protected function getCreateTimeAttr($value)
{
return date('Y-m-d H:i:s', $value);
}
protected function getUserTitleAttr($value,$data)
{
return $data['name'] . ':' . $data['nickname'];
}
}
獲取器方法的第二個(gè)參數(shù)表示當(dāng)前數(shù)據(jù)對(duì)象的所有數(shù)據(jù),數(shù)據(jù)表并不存在user_title
字段链方,其實(shí)獲取器讀取的數(shù)據(jù)對(duì)象屬性和字段是無(wú)關(guān)的持痰。
$user = User::get(1);
// 輸出 thinkphp:流年
echo $user->user_title;
修改器
和獲取器相反,修改器的主要作用是對(duì)模型設(shè)置的數(shù)據(jù)對(duì)象值進(jìn)行處理祟蚀。修改器方法的命名規(guī)范為:
setFieldNameAttr
修改器的使用場(chǎng)景和讀取器類似:
- 時(shí)間日期字段的轉(zhuǎn)換寫入工窍;
- 集合或枚舉類型的寫入割卖;
- 數(shù)字狀態(tài)字段的寫入;
- 某個(gè)字段涉及其它字段的條件或者組合寫入患雏;
定義了修改器之后會(huì)在下列情況下觸發(fā):
- 模型對(duì)象賦值鹏溯;
- 調(diào)用模型的
data
方法,并且第二個(gè)參數(shù)傳入true
淹仑; - 調(diào)用模型的
save
方法丙挽,并且傳入數(shù)據(jù); - 顯式調(diào)用模型的
setAttr
方法匀借; - 定義了該字段的自動(dòng)完成颜阐;
還是用之前的例子,比如說(shuō)你對(duì)User模型的birthday
屬性設(shè)置了一個(gè)2017-1-1
的日期字符串吓肋,但是數(shù)據(jù)表的字段類型是時(shí)間戳類型的凳怨,普通的方式是:
$user = User::get(1);
$user->birthday = strtotime('2017-1-1');
$user->save();
如果使用修改器定義,首先定義
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected function getBirthdayAttr($value)
{
return date('Y-m-d', $value);
}
protected function setBirthdayAttr($value)
{
return strtotime($value);
}
}
通常讀取器和修改器都是配套定義的是鬼,現(xiàn)在我們不需要對(duì)日期數(shù)據(jù)進(jìn)行處理了肤舞,直接使用:
$user = User::get(1);
$user->birthday = '2017-1-1';
// 實(shí)際寫入數(shù)據(jù)表的值是
$user->save();
同樣胸梆,如果你需要在修改器中使用其它屬性的值畸冲,可以添加第二個(gè)參數(shù)盏浇,例如:
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected function setUserTokenAttr($value, $data)
{
return md5($data['name'] . $data['birthday']);
}
}
這里傳入的
data
可能已經(jīng)經(jīng)過(guò)了其它的修改器操作铣减,并非原始的數(shù)據(jù)攒发。
定義后的設(shè)置代碼如下:
$user = User::get(1);
$user->birthday = '2017-1-1';
$user->user_token = null;
$user->save();
由于已經(jīng)定義了user_token
字段的修改器宛官,而改修改器并沒(méi)有根據(jù)當(dāng)前字段的值來(lái)處理帚豪,因此設(shè)置任何值都不影響空免,這里設(shè)置為null
紫皇。和讀取器不同慰安,修改器的屬性必須是數(shù)據(jù)表中存在的字段,否則修改器的值僅僅能作為數(shù)據(jù)輔助作用聪铺。
下面的寫法是錯(cuò)誤的(會(huì)拋出字段不存在的異常)
$user = User::get(1);
$user->birthday = '2017-1-1';
$user->userToken = null;
$user->save();
讀取器是可以使用$user->userToken
來(lái)獲取化焕,為了避免類似問(wèn)題的困惑,我們的建議是:
- 數(shù)據(jù)表字段統(tǒng)一使用小寫+下劃線命名铃剔;
- 方法中統(tǒng)一使用駝峰法命名撒桨;
- 模型的對(duì)象屬性統(tǒng)一使用小寫+下劃線命名;
但實(shí)際應(yīng)用開(kāi)發(fā)過(guò)程中键兜,修改器的定義往往少于讀取器的定義數(shù)量凤类。同時(shí)也要避免多次修改的問(wèn)題。
自動(dòng)時(shí)間字段
由于數(shù)據(jù)表的時(shí)間字段是一個(gè)非常普遍的需求普气,因此框架做了一些強(qiáng)化支持谜疤,無(wú)需定義獲取器和修改器就能完成時(shí)間日期類型字段的自動(dòng)處理。
默認(rèn)情況下自動(dòng)寫入時(shí)間戳字段功能是關(guān)閉的,可以在模型里面定義
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
// 開(kāi)啟時(shí)間字段自動(dòng)寫入
protected $autoWriteTimestamp = true;
}
開(kāi)啟時(shí)間字段(這里的時(shí)間字段支持整型夷磕、時(shí)間戳和日期類型)自動(dòng)寫入后履肃,會(huì)默認(rèn)自動(dòng)寫入兩個(gè)時(shí)間字段:create_time
(創(chuàng)建時(shí)間,新增數(shù)據(jù)的時(shí)候自動(dòng)寫入)和update_time
(更新時(shí)間坐桩,新增和更新的時(shí)候都會(huì)自動(dòng)寫入)尺棋,并且以整型類型寫入數(shù)據(jù)庫(kù)。
如果你的時(shí)間字段名不是默認(rèn)字段撕攒,則需要添加屬性設(shè)置陡鹃。
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
// 開(kāi)啟時(shí)間字段自動(dòng)寫入
protected $autoWriteTimestamp = true;
// 定義時(shí)間字段名
protected $createTime = 'create_at';
protected $updateTime = 'update_at';
}
如果你的時(shí)間字段類型不是整型,也可以單獨(dú)設(shè)置如下:
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
// 開(kāi)啟時(shí)間字段自動(dòng)寫入 并設(shè)置字段類型為datetime
protected $autoWriteTimestamp = 'datetime';
}
autoWriteTimestamp
屬性支持設(shè)置的時(shí)間字段類型包括:整型(設(shè)置為true
的時(shí)候使用該類型)抖坪、時(shí)間(datetime
)和時(shí)間戳(timestamp
)萍鲸。
現(xiàn)在來(lái)看一個(gè)例子說(shuō)明自動(dòng)時(shí)間字段的寫入
// 新增用戶數(shù)據(jù)
$user = new User;
$user->name = 'thinkphp';
// 會(huì)自動(dòng)寫入create_time和update_time字段
$user->save();
echo $user->create_time;
echo $user->update_time;
// 更新用戶數(shù)據(jù)
$user->name = 'topthink';
// 會(huì)自動(dòng)更新update_time字段
$user->save();
echo $user->create_time;
echo $user->update_time;
create_time
和update_time
字段的值不需要進(jìn)行設(shè)置,系統(tǒng)會(huì)自動(dòng)寫入擦俐。如果你手動(dòng)進(jìn)行設(shè)置的話脊阴,則不會(huì)觸發(fā)自動(dòng)寫入機(jī)制(也就是說(shuō)不會(huì)進(jìn)行時(shí)間字段的格式轉(zhuǎn)換),你需要按照實(shí)際的字段類型設(shè)置蚯瞧。
如果你的時(shí)間字段類型為整型嘿期,自動(dòng)寫入的時(shí)間字段會(huì)在獲取的時(shí)候自動(dòng)轉(zhuǎn)換為
dateFormat
屬性設(shè)置的時(shí)間格式,所以不需要再次對(duì)時(shí)間字段進(jìn)行格式化輸出埋合,以免出錯(cuò)备徐。如果不希望自動(dòng)格式化,可以設(shè)置數(shù)據(jù)庫(kù)配置參數(shù)datetime_format
的值為false
甚颂。(時(shí)間類型字段則無(wú)需更改設(shè)置)
我們來(lái)改變下時(shí)間字段的輸出格式蜜猾,
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
// 開(kāi)啟時(shí)間字段自動(dòng)寫入 并設(shè)置字段類型為datetime
protected $autoWriteTimestamp = 'datetime';
protected $dateFormat = 'Y/m/d H:i:s';
}
再來(lái)看輸出的值是否有變化
// 新增用戶數(shù)據(jù)
$user = new User;
$user->name = 'thinkphp';
// 會(huì)自動(dòng)寫入create_time和update_time字段
$user->save();
echo $user->create_time;
echo $user->update_time;
// 更新用戶數(shù)據(jù)
$user->name = 'topthink';
// 會(huì)自動(dòng)更新update_time字段
$user->save();
echo $user->create_time;
echo $user->update_time;
上面的設(shè)置都是針對(duì)單個(gè)模型的,如果需要設(shè)置全局使用振诬,可以在數(shù)據(jù)庫(kù)配置文件中設(shè)置下面的參數(shù):
// 開(kāi)啟自動(dòng)寫入時(shí)間字段 支持設(shè)置字段類型(同前)
'auto_timestamp' => true,
// 時(shí)間字段取出后的時(shí)間格式
'datetime_format' => 'Y-m-d H:i:s',
如果全局設(shè)置開(kāi)啟時(shí)間字段自動(dòng)寫入后蹭睡,部分模型可以單獨(dú)關(guān)閉,例如:
<?php
namespace app\index\model;
use think\Model;
class Data extends Model
{
// 關(guān)閉時(shí)間字段自動(dòng)寫入
protected $autoWriteTimestamp = false;
}
甚至說(shuō)部分模型的時(shí)間字段名和類型可以單獨(dú)設(shè)置
<?php
namespace app\index\model;
use think\Model;
class Data extends Model
{
// 關(guān)閉時(shí)間字段自動(dòng)寫入
protected $autoWriteTimestamp = 'datetime';
// 定義時(shí)間字段名
protected $createTime = 'create_at';
protected $updateTime = 'update_at';
}
在系統(tǒng)自動(dòng)時(shí)間字段之外的其它時(shí)間字段赶么,如果需要自動(dòng)格式輸出肩豁,可以設(shè)置類型轉(zhuǎn)換,這個(gè)下一節(jié)就會(huì)講到辫呻。
數(shù)據(jù)類型轉(zhuǎn)換
自動(dòng)時(shí)間字段寫入只支持創(chuàng)建時(shí)間和更新時(shí)間的自動(dòng)寫入和格式化讀取清钥,如果你的模型有其它時(shí)間字段的話,則可以通過(guò)設(shè)置類型轉(zhuǎn)換來(lái)完成放闺,例如User
模型的birthday
字段也使用了時(shí)間類型循捺。
前面我們已經(jīng)了解了定義修改器和讀取器的方式來(lái)處理birthday
字段,更簡(jiǎn)單的辦法就是上面這種設(shè)置類型轉(zhuǎn)換雄人,免去定義修改器和讀取器的麻煩。
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected $type = [
'birthday' => 'datetime:Y/m/d',
];
}
type
屬性用于定義類型轉(zhuǎn)換(支持相當(dāng)多的類型,后面會(huì)提及)础钠,'datetime:Y/m/d'
表示使用datetime
類型恰力,輸出格式為Y/m/d
,下面是一段代碼示例旗吁。
$user = User::get(1);
// 輸出 2009/02/14
echo $user->birthday;
$user->birthday = '2017-1-1';
$user->save();
// 輸出 2017/1/1
echo $user->birthday;
類型轉(zhuǎn)換支持的類型設(shè)置包括:
integer
設(shè)置為integer(整型)后踩萎,該字段寫入和輸出的時(shí)候都會(huì)自動(dòng)轉(zhuǎn)換為整型。
float
該字段的值寫入和輸出的時(shí)候自動(dòng)轉(zhuǎn)換為浮點(diǎn)型很钓。
boolean
該字段的值寫入和輸出的時(shí)候自動(dòng)轉(zhuǎn)換為布爾型香府。
array
如果設(shè)置為強(qiáng)制轉(zhuǎn)換為array
類型,系統(tǒng)會(huì)自動(dòng)把數(shù)組編碼為json格式字符串寫入數(shù)據(jù)庫(kù)码倦,取出來(lái)的時(shí)候會(huì)自動(dòng)解碼企孩。
object
該字段的值在寫入的時(shí)候會(huì)自動(dòng)編碼為json字符串,輸出的時(shí)候會(huì)自動(dòng)轉(zhuǎn)換為stdclass
對(duì)象袁稽。
serialize
指定為序列化類型的話勿璃,數(shù)據(jù)會(huì)自動(dòng)序列化寫入,并且在讀取的時(shí)候自動(dòng)反序列化推汽。
json
指定為json
類型的話补疑,數(shù)據(jù)會(huì)自動(dòng)json_encode
寫入,并且在讀取的時(shí)候自動(dòng)json_decode
處理歹撒。
timestamp
指定為時(shí)間戳字段類型(注意并不是數(shù)據(jù)庫(kù)的timestamp
類型莲组,事實(shí)上是int
類型)的話,該字段的值在寫入時(shí)候會(huì)自動(dòng)使用strtotime
生成對(duì)應(yīng)的時(shí)間戳暖夭,輸出的時(shí)候會(huì)自動(dòng)轉(zhuǎn)換為dateFormat
屬性定義的時(shí)間字符串格式锹杈,默認(rèn)的格式為Y-m-d H:i:s
,如果希望改變其他格式鳞尔,可以定義如下:
class User extends Model
{
protected $dateFormat = 'Y/m/d';
protected $type = [
'status' => 'integer',
'score' => 'float',
'birthday' => 'timestamp',
];
}
或者在類型轉(zhuǎn)換定義的時(shí)候使用:
class User extends Model
{
protected $type = [
'status' => 'integer',
'score' => 'float',
'birthday' => 'timestamp:Y/m/d',
];
}
然后就可以
$user = User::find(1);
echo $user->birthday; // 2015/5/1
datetime
和timestamp
類似嬉橙,區(qū)別在于寫入和讀取數(shù)據(jù)的時(shí)候都會(huì)自動(dòng)處理成時(shí)間字符串Y-m-d H:i:s
的格式。
PHP5.6版本以下寥假,數(shù)據(jù)庫(kù)查詢的字段返回?cái)?shù)據(jù)類型都是字符串的市框,在做API開(kāi)發(fā)的時(shí)候最好是使用類型轉(zhuǎn)換強(qiáng)制處理下,PHP5.6版本開(kāi)始糕韧,PDO查詢的返回?cái)?shù)據(jù)的字段類型都是實(shí)際的字段類型格式枫振。
數(shù)據(jù)自動(dòng)完成
前面提到的修改器是在設(shè)置數(shù)據(jù)的時(shí)候自動(dòng)觸發(fā),有時(shí)候我們希望數(shù)據(jù)修改是自動(dòng)觸發(fā)的萤彩,類似于自動(dòng)時(shí)間字段一樣(只能自動(dòng)完成固定時(shí)間字段)粪滤,這就是本節(jié)要講解的數(shù)據(jù)自動(dòng)完成的概念。
數(shù)據(jù)自動(dòng)完成是依賴修改器的(和3.2版本區(qū)別很大)雀扶,不支持使用函數(shù)或者其它回調(diào)來(lái)自動(dòng)完成(但可以支持固定值)杖小,足見(jiàn)5.0版本是推崇使用修改器肆汹。
系統(tǒng)支持auto
、insert
和update
三個(gè)屬性予权,可以分別設(shè)置寫入昂勉、新增和更新的時(shí)候需要進(jìn)行自動(dòng)完成的字段列表(auto
屬性包含新增和更新操作),下面是一個(gè)示例:
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected $auto = ['active_time'];
protected $insert = ['reg_ip', 'status' => 1];
protected $update = [];
}
定義了數(shù)據(jù)自動(dòng)完成后不需要手動(dòng)設(shè)置屬性扫腺,一旦使用手動(dòng)設(shè)置的話岗照,自動(dòng)完成就會(huì)忽略,以避免產(chǎn)生多次處理的數(shù)據(jù)混亂笆环。
數(shù)據(jù)轉(zhuǎn)換和輸出
數(shù)組轉(zhuǎn)換
模型的查詢返回?cái)?shù)據(jù)都是模型的對(duì)象實(shí)例攒至,其實(shí)模型對(duì)象在數(shù)據(jù)存取方面和數(shù)組幾乎沒(méi)什么差異,不信的話躁劣,你可以測(cè)試下下面的一段代碼:
$user = User::get(1);
echo $user->name;
echo $user['name'];
$user->name = 'test';
$user->save();
echo $user->name;
$user['name'] = 'test2';
$user->save();
echo $user['name'];
即使在模板中輸出迫吐,也可以正常使用:
{$user.name}
{$name.email}
答案是由于模型類think\Model
實(shí)現(xiàn)了ArrayAccess
接口,所以對(duì)象可以采用數(shù)組的方式訪問(wèn)习绢。
如果是數(shù)據(jù)集查詢渠抹,也是和數(shù)組一樣foreach
遍歷后使用
$list = User::all();
foreach ($list as $user) {
echo $user->name . ':' . $user['name'];
}
// 當(dāng)然如果你需要也可以直接
echo $list[0]->name . ':' . $list[0]['name'];
所以,看起來(lái)模型查詢返回的對(duì)象對(duì)使用上并沒(méi)有任何的影響闪萄。
如果因?yàn)槟撤N原因梧却,必須要轉(zhuǎn)換為數(shù)組的話,可以使用模型類提供的toArray
方法败去,用法如下:
$user = User::get(1);
if($user) {
$data = $user->toArray();
}
如果是數(shù)據(jù)集查詢的話有兩種情況放航,由于默認(rèn)的數(shù)據(jù)集返回結(jié)果的類型是一個(gè)數(shù)組,因此無(wú)法調(diào)用toArray
方法圆裕,必須先轉(zhuǎn)成數(shù)據(jù)集對(duì)象然后再使用toArray
方法广鳍,系統(tǒng)提供了一個(gè)collection
助手函數(shù)實(shí)現(xiàn)數(shù)據(jù)集對(duì)象的轉(zhuǎn)換,代碼如下:
$list = User::all();
if($list) {
$list = collection($list)->toArray();
}
如果設(shè)置了模型的數(shù)據(jù)集返回類型的話吓妆,則可以簡(jiǎn)化使用
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected $resultSetType = 'collection';
}
關(guān)鍵代碼是在模型中添加下面一行定義
protected $resultSetType = 'collection';
設(shè)置后赊时,模型所有的數(shù)據(jù)集查詢返回結(jié)果的類型都是think\model\Collection
對(duì)象實(shí)例。
下面的事情就簡(jiǎn)單了行拢,直接使用:
$list = User::all();
$list = $list->toArray();
當(dāng)然toArray
方法不僅僅只是轉(zhuǎn)換一個(gè)數(shù)組這么簡(jiǎn)單祖秒,我們可以在轉(zhuǎn)換數(shù)據(jù)的時(shí)候進(jìn)行個(gè)別字段的隱藏和追加,涉及到四個(gè)方法:
方法 | 說(shuō)明 |
---|---|
hidden |
設(shè)置隱藏的屬性 |
visible |
設(shè)置輸出的屬性 |
append |
追加額外的(獲取器)屬性 |
appendRelationAttr |
追加額外的關(guān)聯(lián)屬性 |
這幾個(gè)方法都是針對(duì)模型對(duì)象示例的(數(shù)據(jù)集對(duì)象無(wú)法使用)舟奠,前三個(gè)方法的參數(shù)都是數(shù)組竭缝,而且默認(rèn)會(huì)和模型類的hidden
、visible
以及append
屬性合并沼瘫。下面是一個(gè)代碼示例:
$user = User::get(1);
$data = $user->hidden(['id'])->toArray();
dump($data);
$data = $user->visible(['name', 'email'])->toArray();
dump($data);
$data = $user->append(['status_text'])->toArray();
dump($data);
hidden
和visible
方法設(shè)置的是當(dāng)前模型對(duì)應(yīng)的數(shù)據(jù)表中的字段列表中需要隱藏或者顯示的屬性抬纸,如果不在數(shù)據(jù)表字段列表中,但設(shè)置過(guò)獲取器的話耿戚,可以通過(guò)append
方法追加湿故。如果不在字段列表中阿趁,也沒(méi)有設(shè)置過(guò)任何的獲取器,則只能手動(dòng)賦值晓锻,例如:
$user = User::get(1);
$user->hello = 'thinkphp';
$data = $user->toArray();
dump($data);
如果已經(jīng)在模型中設(shè)置了hidden
歌焦、visible
或者append
屬性,但臨時(shí)不需要輸出砚哆,可以在方法中傳入第二個(gè)參數(shù)true
設(shè)置為覆蓋而不是合并。
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected $hidden = ['id'];
protected $visible = ['name', 'email'];
protected $append = ['level'];
protected function getLevelAttr($value, $data)
{
$score = $data['score'];
if ($score < 100) {
$level = 1;
} elseif ($score < 500) {
$level = 2;
} elseif ($score < 2000) {
$level = 3;
} elseif ($score < 5000) {
$level = 4;
} else {
$level = 5;
}
return $level;
}
}
下面是測(cè)試代碼:
$user = User::get(1);
$data = $user->hidden(['email'], true)->toArray();
dump($data);
$data = $user->visible(['name'], true)->toArray();
dump($data);
$data = $user->append(['level'], true)->toArray();
dump($data);
appendRelationAttr
方法是用于追加關(guān)聯(lián)對(duì)象中的某個(gè)屬性到當(dāng)前模型對(duì)象屬性中屑墨,暫且不表躁锁,會(huì)在關(guān)聯(lián)和聚合章節(jié)中給你詳細(xì)講解用法。
如果是數(shù)據(jù)集查詢的話卵史,只能通過(guò)在模型類中設(shè)置屬性的方式來(lái)設(shè)置需要輸出的屬性战转,同樣是上面的User模型類,用下面的代碼測(cè)試:
$list = User::all();
$data = $list->toArray();
dump($data);
JSON序列化
除了轉(zhuǎn)換為數(shù)組數(shù)據(jù)外以躯,還支持對(duì)模型對(duì)象進(jìn)行JSON
序列化槐秧,序列化方法為toJson
,使用方法和toArray
類似忧设,并且調(diào)用toJson
序列化之前同樣支持hidden
刁标、visible
、append
和appendRelationAttr
方法址晕。
模型事件
雖然說(shuō)模型的數(shù)據(jù)自動(dòng)完成和修改器可以很方便的進(jìn)行數(shù)據(jù)處理膀懈,但也有自身的不足,而模型事件功能則提供了更靈活和強(qiáng)大的模型數(shù)據(jù)處理機(jī)制谨垃。
模型事件可以看成是模型層的鉤子和行為启搂,只不過(guò)鉤子的位置主要針對(duì)模型數(shù)據(jù)的寫入操作,包含下面這些:
鉤子 | 對(duì)應(yīng)操作 | 快捷注冊(cè)方法 |
---|---|---|
before_insert | 新增前 | beforeInsert |
after_insert | 新增后 | afterInsert |
before_update | 更新前 | beforeUpdate |
after_update | 更新后 | afterUpdate |
before_write | 寫入前 | beforeWrite |
after_write | 寫入后 | afterWrite |
before_delete | 刪除前 | beforeDelete |
after_delete | 刪除后 | afterDelete |
before_write
和after_write
表示無(wú)論是新增還是更新都會(huì)執(zhí)行的鉤子刘陶。
要使用模型事件功能胳赌,就必須先給模型注冊(cè)事件,我們建議在模型類的init
方法中統(tǒng)一注冊(cè)模型事件(init
靜態(tài)方法會(huì)在實(shí)例化模型的時(shí)候調(diào)用匙隔,并且僅會(huì)執(zhí)行一次)疑苫,下面是一個(gè)例子,在寫入用戶數(shù)據(jù)的時(shí)候牡直。
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected static function init()
{
User::event('before_insert', function ($user) {
$user->reg_ip = request()->ip();
});
User::event('before_write', function ($user) {
$user->name = strtolower($user->name);
});
}
}
為了更簡(jiǎn)單的定義缀匕,系統(tǒng)提供了快捷方法,上面的模型事件注冊(cè)可以改為:
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
protected static function init()
{
User::beforeInsert(function ($user) {
$user->reg_ip = request()->ip();
});
User::beforeWrite(function ($user) {
$user->name = strtolower($user->name);
});
}
}
在事件方法中可以對(duì)當(dāng)前模型實(shí)例做任何的操作碰逸,所以理論上來(lái)說(shuō)乡小,修改器和自動(dòng)完成可以做的事情,模型的事件方法都可以完成饵史,而且具備修改器和自動(dòng)完成沒(méi)有的優(yōu)勢(shì)满钟,包括:
- 批量完成(修改器只能針對(duì)某個(gè)字段進(jìn)行修改)胜榔;
- 支持判斷并自動(dòng)終止模型數(shù)據(jù)寫入操作;
- 便于統(tǒng)一管理模型數(shù)據(jù)操作湃番;
不要對(duì)一個(gè)模型數(shù)據(jù)同時(shí)使用修改器和模型事件
注冊(cè)的回調(diào)方法的第一個(gè)參數(shù)是當(dāng)前的模型對(duì)象實(shí)例夭织,并且所有before
開(kāi)頭的事件方法(包括before_write
、before_insert
吠撮、 before_update
尊惰、before_delete
)如果返回false
,則寫入操作不會(huì)執(zhí)行泥兰,并且也不再執(zhí)行該鉤子位置的后續(xù)事件操作弄屡。
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
public function isValid()
{
}
protected static function init()
{
User::event('before_insert', function ($user) {
$user->reg_ip = request()->ip();
});
User::event('before_write', function ($user) {
return $user->isValid();
});
}
}
支持給一個(gè)位置注冊(cè)多個(gè)回調(diào)方法,例如:
User::event('before_insert', function ($user) {
if ($user->status != 1) {
return false;
}
});
// 注冊(cè)回調(diào)到beforeInsert函數(shù)
User::event('before_insert', 'beforeInsert');
當(dāng)同一個(gè)鉤子注冊(cè)了多個(gè)模型事件的話鞋诗,在特殊的情況下膀捷,你可以傳入第二個(gè)參數(shù)為true
覆蓋之前注冊(cè)的其它模型事件。
User::event('before_insert', function ($user) {
if ($user->status != 1) {
return false;
}
});
// 注冊(cè)回調(diào)到beforeInsert函數(shù) 并覆蓋前面的
User::event('before_insert', 'beforeInsert',true);
數(shù)據(jù)分批處理
模型也可以支持對(duì)返回的數(shù)據(jù)分批處理削彬,并且由于返回的是模型對(duì)象全庸,更方便進(jìn)行業(yè)務(wù)操作,例如:
$count = 0;
User::chunk(100, function ($users) use ($event,$count) {
foreach ($users as $user) {
// 用戶活動(dòng)報(bào)名
if ($user->age > 18) {
$user->sign($event);
$count++;
if ($count >= 300) {
// 超過(guò)300則不再接受報(bào)名
return false;
}
}
}
});
總結(jié)
數(shù)據(jù)寫入的自動(dòng)處理規(guī)則及優(yōu)先順序如下:
- 時(shí)間字段自動(dòng)格式化寫入
- 修改器
- 類型轉(zhuǎn)換
- 模型事件
數(shù)據(jù)讀取的自動(dòng)處理規(guī)則及優(yōu)先順序如下:
- 獲取器
- 類型轉(zhuǎn)換
- 時(shí)間字段自動(dòng)格式化輸出
通過(guò)本章的學(xué)習(xí)融痛,我們基本掌握了模型數(shù)據(jù)的處理和轉(zhuǎn)換壶笼、輸出,下一章我們來(lái)學(xué)習(xí)模型的一些高級(jí)用法酌心。
上一篇:第五章:模型和對(duì)象
下一篇:第七章:模型高級(jí)用法