Thinkphp 第七章:模型高級(jí)用法

本章帶你了解下模型的一些高級(jí)技巧具篇,這些技巧會(huì)讓你在使用模型的過程中更加高效和簡(jiǎn)單,學(xué)習(xí)內(nèi)容主要包含:

條件查詢

我們前面的大多數(shù)例子都是使用主鍵進(jìn)行模型數(shù)據(jù)查詢马昙,事實(shí)上,業(yè)務(wù)邏輯并非都是如此簡(jiǎn)單,條件查詢的必要性是存在的帆调。

最簡(jiǎn)單的方法是直接使用Db類的查詢構(gòu)造器查詢,方法是在模型中靜態(tài)調(diào)用Db類(其實(shí)是查詢類)的任何方法(包括動(dòng)態(tài)查詢)進(jìn)行查詢豆同。

// 查詢單個(gè)記錄
User::where('name', 'thinkphp')->find();
// 調(diào)用動(dòng)態(tài)查詢方法
User::getByName('thinkphp');
// 查詢數(shù)據(jù)集
User::where('id', '>', 0)->limit(10)->order('id desc')->select();
// 刪除數(shù)據(jù)
User::where('status', 0)->delete();

如果你的查詢代碼在模型內(nèi)部番刊,可以直接支持動(dòng)態(tài)方式調(diào)用查詢構(gòu)造器的方法,用法就是從靜態(tài)調(diào)用改為動(dòng)態(tài)調(diào)用诱告。

// 查詢單個(gè)記錄
$this->where('name', 'thinkphp')->find();
// 調(diào)用動(dòng)態(tài)查詢方法
$this->getByName('thinkphp');
// 查詢數(shù)據(jù)集
$this->where('id', '>', 0)->limit(10)->order('id desc')->select();
// 刪除數(shù)據(jù)
$this->where('status', 0)->delete();

不過類似的查詢場(chǎng)景應(yīng)當(dāng)極力避免(個(gè)別場(chǎng)景例如查詢范圍等可能會(huì)涉及到此類用法)撵枢,查詢操作應(yīng)當(dāng)是靜態(tài)調(diào)用,更新和刪除操作則是動(dòng)態(tài)方法調(diào)用精居。如果是在模型方法中查詢其它模型的數(shù)據(jù)锄禽,第一種靜態(tài)調(diào)用方式仍然適用。尤其不建議使用table方法在同一個(gè)模型實(shí)例中切換數(shù)據(jù)表查詢靴姿,在模型中動(dòng)態(tài)設(shè)置table屬性的方式更加不可任值(經(jīng)常發(fā)現(xiàn)這種奇葩的用法),模型和數(shù)據(jù)表以及相應(yīng)的業(yè)務(wù)邏輯是應(yīng)當(dāng)在創(chuàng)建的時(shí)候就相對(duì)固定的佛吓,應(yīng)當(dāng)極力避免在一個(gè)模型對(duì)象實(shí)例中查詢操作多次不同數(shù)據(jù)宵晚。

模型查詢的原則應(yīng)當(dāng)是每個(gè)模型對(duì)象實(shí)例操作一個(gè)唯一記錄,對(duì)于數(shù)據(jù)集來(lái)說(shuō)這個(gè)原則也不變维雇,只是每個(gè)數(shù)據(jù)集對(duì)象實(shí)例則包含多個(gè)模型對(duì)象實(shí)例而已淤刃。

對(duì)于自定義查詢,如果統(tǒng)一使用模型類提供的getall進(jìn)行查詢也一樣可以達(dá)到目的吱型,改成下面代碼即可:

// 查詢單個(gè)記錄
User::get(['name' => 'thinkphp']);
// 查詢數(shù)據(jù)集
User::all(function ($query) {
    $query->where('id', '>', 0)
        ->limit(10)
        ->order('id desc');
});
// 刪除數(shù)據(jù)
User::destroy(['status' => 0]);
User::destroy(function ($query) {
    $query->where('id', 'in', [1, 2, 3]);
});

get逸贾、all以及destroy方法的參數(shù)用法記住一個(gè)原則,如果是數(shù)字津滞、字符串或者普通數(shù)組都表示一個(gè)或者多個(gè)主鍵铝侵,如果是索引數(shù)組則表示查詢條件,閉包則支持查詢條件以外的其它鏈?zhǔn)讲僮鞣椒ùバ臁?duì)于get方法的參數(shù)最好做一次非null檢查咪鲜,否則查詢的就會(huì)是第一個(gè)數(shù)據(jù)(V5.0.8+已經(jīng)改進(jìn),不需要檢查是否為null了)撞鹉。

查詢范圍

對(duì)于一些常用的查詢條件疟丙,我們可以事先定義好颖侄,以便快速調(diào)用,這個(gè)事先定義的查詢條件方法有一個(gè)統(tǒng)一的前綴scope隆敢,我們稱之為查詢范圍发皿,例如下面給User模型定義了兩個(gè)查詢范圍方法。

<?php

namespace app\index\model;

use think\Model;

class User extends Model
{

    // email查詢
    protected function scopeEmail($query)
    {
        $query->where('email', 'thinkphp@qq.com');
    }

    // status查詢
    protected function scopeStatus($query)
    {
        $query->where('status', 1);
    }
}

現(xiàn)在我們直接使用

$users = User::scope('email,status')->select();

或者使用

$users = User::scope('email')->scope('status')->select();

生成的查詢語(yǔ)句是

SELECT * FROM `user` WHERE `email` = 'thinkphp@qq.com' AND `status` = 1 

查詢范圍之外仍然可以使用額外的查詢條件拂蝎,例如:

$users = User::scope('email,status')
    ->where('nickname', 'like', '%think%')
    ->order('id desc')
    ->select();

查詢范圍方法必須首先被調(diào)用

查詢范圍方法支持額外的參數(shù)穴墅,例如scopeEmail方法改為:

    // email查詢
    protected function scopeEmail($query, $email = '')
    {
        $query->where('email', $email);
    }

查詢范圍的方法的第一個(gè)參數(shù)必須是查詢對(duì)象,并且支持多個(gè)額外參數(shù)温自。

然后玄货,使用下面的方式調(diào)用即可(帶參數(shù)調(diào)用的時(shí)候每次只能調(diào)用一個(gè)查詢范圍):

$list = User::scope('email', 'thinkphp@qq.com')->select();

查詢范圍有一個(gè)特殊的方法base,一旦在模型中定義了base方法后悼泌,無(wú)需顯式調(diào)用scope方法松捉,系統(tǒng)會(huì)在每次查詢的時(shí)候自動(dòng)調(diào)用,我們稱之為全局查詢范圍馆里。

<?php

namespace app\index\model;

use think\Model;

class User extends Model
{
    // 全局查詢范圍
    protected static function base($query)
    {
        // 查詢狀態(tài)為1的數(shù)據(jù)
        $query->where('status', 1);
    }

    // email查詢
    protected function scopeEmail($query)
    {
        $query->where('email', 'thinkphp@qq.com');
    }

}

當(dāng)使用下面的查詢操作

User::get(1);
User::scope('email')->select();

最后生成的SQL語(yǔ)句分別是:

SELECT * FROM `user` WHERE `status` = 1 AND `id` = 1 LIMIT 1    
SELECT * FROM `user` WHERE `status` = 1 AND `email` = 'thinkphp@qq.com' 

無(wú)論是什么查詢都會(huì)默認(rèn)帶上全局查詢范圍中的條件隘世。

可以臨時(shí)關(guān)閉全局查詢范圍進(jìn)行查詢

// 關(guān)閉全局查詢范圍
User::useGlobalScope(false)->get(1);

查詢范圍方法中不僅支持where方法,任何查詢構(gòu)造器的方法都可以被支持鸠踪。

字段過濾

經(jīng)常我們會(huì)直接使用表單提交的數(shù)據(jù)來(lái)作為模型數(shù)據(jù)寫入丙者,但并不是所有的數(shù)據(jù)都是數(shù)據(jù)表字段(直接寫入會(huì)導(dǎo)致數(shù)據(jù)庫(kù)異常),或者不希望某些數(shù)據(jù)被用戶通過表單提交的方式更新(為了安全或者邏輯考慮)营密,Request類自身提供了only方法來(lái)獲取部分想要的數(shù)據(jù)械媒,例如:

// 只獲取請(qǐng)求變量中的nickname和address變量
$data = request()->only(['nickname', 'address']);
// 獲取當(dāng)前用戶對(duì)象
$user = User::get(request()->session('user_id'));
// 更新用戶數(shù)據(jù)
$user->data($data, true)->save();

模型類提供了allowField方法用于在數(shù)據(jù)寫入操作的時(shí)候設(shè)置字段過濾,從而避免數(shù)據(jù)庫(kù)因?yàn)樽侄尾淮嬖诙鴪?bào)錯(cuò)评汰,上面的寫法可以簡(jiǎn)化為纷捞。

// 獲取當(dāng)前用戶對(duì)象
$user = User::get(request()->session('user_id'));
// 只允許更新用戶的nickname和address數(shù)據(jù)
$user->allowField(['nickname', 'address'])
    ->data(requst()->param(), true)
    ->save();

如果僅僅是希望去除數(shù)據(jù)表之外的字段,可以使用

// 只允許更新數(shù)據(jù)表字段數(shù)據(jù)
$user->allowField(true)
    ->data(requst()->param(), true)
    ->save();

為了不必每次都調(diào)用allowField方法被去,我們可以直接在模型類里面設(shè)置field屬性主儡,例如:

<?php

namespace app\index\model;

use think\Model;

class User extends Model
{
    protected $field = ['name', 'nickname', 'email', 'address'];
}

當(dāng)調(diào)用allowField方法的時(shí)候,當(dāng)前模型實(shí)例中的該配置的值會(huì)被覆蓋惨缆。

如果使用的是模型的靜態(tài)方法(如createupdate方法)進(jìn)行數(shù)據(jù)寫入的話糜值,可以使用下面的方式進(jìn)行字段過濾。

User::create(request()->param(), ['nickname', 'address']);
User::update(request()->param(), ['id' => 1], ['nickname', 'address']);

同樣可以傳入true表示過濾非數(shù)據(jù)表字段

User::create(request()->param(), true);
User::update(request()->param(), ['id' => 1], true);

只讀字段

有些數(shù)據(jù)字段在寫入以后就不允許被更改踪央,例如name字段和email字段臀玄,那么我們可以設(shè)置該字段為只讀字段瓢阴,在更新的時(shí)候就會(huì)被自動(dòng)忽略掉畅蹂。

設(shè)置方式:

<?php

namespace app\index\model;

use think\Model;

class User extends Model
{
    protected $readonly = ['name','email'];
}

舉個(gè)例子說(shuō)明下:

$user = User::get(5);
echo $user->name;
echo $user->email;
// 更改某些字段的值
$user->name    = 'TOPThink';
$user->email   = 'Topthink@gmail.com';
$user->address = '上海靜安區(qū)';
// 保存更改后的用戶數(shù)據(jù)
$user->save();
echo $user->name;
echo $user->email;

事實(shí)上,由于我們對(duì)nameemail字段設(shè)置了只讀荣恐,因此只有address字段的值被更新了液斜,而nameemail的值仍然還是更新之前的值累贤。

軟刪除

在實(shí)際項(xiàng)目中,對(duì)數(shù)據(jù)頻繁使用刪除操作會(huì)導(dǎo)致性能問題少漆,因此不推薦直接物理刪除數(shù)據(jù)臼膏,而是用邏輯刪除替代,也就是下面要講的軟刪除示损。軟刪除的作用就是把數(shù)據(jù)加上刪除標(biāo)記渗磅,而不是真正的刪除,同時(shí)也便于需要的時(shí)候進(jìn)行數(shù)據(jù)的恢復(fù)检访。

要使用軟刪除功能始鱼,需要引入SoftDelete trait,例如User模型按照下面的定義就可以使用軟刪除功能:

<?php

namespace app\index\model;

use think\Model;
use traits\model\SoftDelete;

class User extends Model
{
    use SoftDelete;
}

為了配合軟刪除功能脆贵,你需要在數(shù)據(jù)表中添加delete_time字段医清,ThinkPHP5的軟刪除功能使用時(shí)間戳類型(數(shù)據(jù)表默認(rèn)值為Null),用于記錄數(shù)據(jù)的刪除時(shí)間卖氨。

如果你的軟刪除標(biāo)記字段名稱不是delete_time的話会烙,需要添加屬性定義:

<?php

namespace app\index\model;

use think\Model;
use traits\model\SoftDelete;

class User extends Model
{
    use SoftDelete;
    protected $deleteTime = 'delete_field_name';
}

可以用類型轉(zhuǎn)換指定軟刪除字段的類型,建議數(shù)據(jù)表的所有時(shí)間字段統(tǒng)一使用autoWriteTimestamp屬性規(guī)范時(shí)間類型(支持datetime筒捺、date柏腻、timestamp以及integer)。

<?php

namespace app\index\model;

use think\Model;
use traits\model\SoftDelete;

class User extends Model
{
    use SoftDelete;
    protected $autoWriteTimestamp = 'datetime';
    protected $deleteTime = 'delete_field_name';
}

定義好模型后焙矛,我們就可以使用:

// 軟刪除
User::destroy(1);
// 真實(shí)刪除
User::destroy(1,true);
$user = User::get(1);
// 軟刪除
$user->delete();
// 真實(shí)刪除
$user->delete(true);

默認(rèn)情況下查詢的數(shù)據(jù)不包含軟刪除數(shù)據(jù)葫盼,如果需要包含軟刪除的數(shù)據(jù),可以使用下面的方式查詢:

User::withTrashed()->find();
User::withTrashed()->select();

如果僅僅需要查詢軟刪除的數(shù)據(jù)村斟,可以使用:

User::onlyTrashed()->find();
User::onlyTrashed()->select();

如果你的查詢條件比較復(fù)雜贫导,尤其是某些特殊情況下使用OR查詢條件會(huì)把軟刪除數(shù)據(jù)也查詢出來(lái),可以使用閉包查詢的方式解決蟆盹,如下:

User::where(function($query) {
    $query->where('id', '>', 10)
        ->whereOr('name', 'like', 'think');
})->select();

使用閉包查詢條件會(huì)在查詢條件兩邊添加括號(hào)孩灯,從而不會(huì)和軟刪除條件產(chǎn)生混淆或者沖突。

如果你的模型定義了base基礎(chǔ)查詢逾滥,請(qǐng)確保添加軟刪除的基礎(chǔ)查詢條件峰档,例如:

protected function base($query)
{
    // 添加軟刪除條件
    $query->whereNull('delete_time')
        // 添加額外的基礎(chǔ)查詢條件
        ->where('id','>',0);
}

自定義查詢類

默認(rèn)情況下,默認(rèn)使用的查詢類是核心內(nèi)置的think\db\Query類寨昙,如果你需要自己擴(kuò)展額外的查詢方法讥巡,可以自定義查詢類,例如:

<?php

namespace app\db;

use think\db\Query;

class ModelQuery extends  Query
{

    public function top($num)
    {
        return $this->limit($num)-select();
    }
}

在模型類中設(shè)置query屬性如下

<?php

namespace app\index\model;

use think\Model;

class User extends Model
{
    protected $query = '\app\db\ModelQuery';
}

設(shè)置后舔哪,User模型就可以使用top方法查詢

User::where('id desc')->top(10);

如果全局都使用該查詢類的話欢顷,建議直接在數(shù)據(jù)庫(kù)配置文件中使用下面配置:

// 設(shè)置Query類
'query'           => '\\app\\db\\ModelQuery',

總結(jié)

本章我們學(xué)習(xí)了模型的一些高級(jí)用法,下一章我們就來(lái)學(xué)習(xí)下模型關(guān)聯(lián)的使用捉蚤。

上一篇:第六章:模型數(shù)據(jù)處理
下一篇:第八章:模型關(guān)聯(lián)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抬驴,一起剝皮案震驚了整個(gè)濱河市炼七,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌布持,老刑警劉巖豌拙,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異题暖,居然都是意外死亡按傅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門胧卤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)逞敷,“玉大人,你說(shuō)我怎么就攤上這事灌侣⊥凭瑁” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵侧啼,是天一觀的道長(zhǎng)牛柒。 經(jīng)常有香客問我,道長(zhǎng)痊乾,這世上最難降的妖魔是什么皮壁? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮哪审,結(jié)果婚禮上蛾魄,老公的妹妹穿的比我還像新娘。我一直安慰自己湿滓,他們只是感情好滴须,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著叽奥,像睡著了一般扔水。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上朝氓,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天魔市,我揣著相機(jī)與錄音,去河邊找鬼赵哲。 笑死待德,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的枫夺。 我是一名探鬼主播将宪,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了涧偷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤毙死,失蹤者是張志新(化名)和其女友劉穎燎潮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扼倘,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡确封,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了再菊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爪喘。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖纠拔,靈堂內(nèi)的尸體忽然破棺而出秉剑,到底是詐尸還是另有隱情,我是刑警寧澤稠诲,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布侦鹏,位于F島的核電站,受9級(jí)特大地震影響臀叙,放射性物質(zhì)發(fā)生泄漏略水。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一劝萤、第九天 我趴在偏房一處隱蔽的房頂上張望渊涝。 院中可真熱鬧,春花似錦床嫌、人聲如沸跨释。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)煤傍。三九已至,卻和暖如春嘱蛋,著一層夾襖步出監(jiān)牢的瞬間蚯姆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工洒敏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留龄恋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓凶伙,卻偏偏與公主長(zhǎng)得像郭毕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子函荣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,109評(píng)論 1 32
  • ORA-00001: 違反唯一約束條件 (.) 錯(cuò)誤說(shuō)明:當(dāng)在唯一索引所對(duì)應(yīng)的列上鍵入重復(fù)值時(shí)显押,會(huì)觸發(fā)此異常扳肛。 O...
    我想起個(gè)好名字閱讀 5,333評(píng)論 0 9
  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查詢集API 參...
    陽(yáng)光小鎮(zhèn)少爺閱讀 3,827評(píng)論 0 8
  • 點(diǎn)擊查看原文 Web SDK 開發(fā)手冊(cè) SDK 概述 網(wǎng)易云信 SDK 為 Web 應(yīng)用提供一個(gè)完善的 IM 系統(tǒng)...
    layjoy閱讀 13,783評(píng)論 0 15
  • 已經(jīng)記不得是第幾次搬家了。 看著那些剛剛安置在屋里為數(shù)不多的幾個(gè)物件乘碑,眼眶有些濕潤(rùn)挖息。 真想給他們道個(gè)歉,什么時(shí)候才...
    陳羲1874閱讀 270評(píng)論 0 2