Thinkphp 第三章:查詢構(gòu)造器

本章主要來學(xué)習(xí)和使用查詢構(gòu)造器的用法,掌握查詢構(gòu)造器對于掌握數(shù)據(jù)庫和模型的查詢操作非常關(guān)鍵,學(xué)習(xí)內(nèi)容主要包含:

在第一章我們已經(jīng)學(xué)習(xí)了如何使用原生查詢,不過原生查詢的話就失去了數(shù)據(jù)庫抽象訪問層的意義了,所以數(shù)據(jù)庫抽象訪問層的優(yōu)勢就是使用查詢構(gòu)造器進(jìn)行查詢。

查詢構(gòu)造器就是利用查詢類和生成類完成最終的查詢語句構(gòu)造(注意這里的查詢是一個泛指,包括數(shù)據(jù)庫的讀寫操作)。由于生成類是由查詢類自動調(diào)用的,可以說查詢構(gòu)造器的用法其實就是查詢類的用法,因此首先我們必須清楚查詢類怎么調(diào)用或者說是在什么時候調(diào)用。

為了方便學(xué)習(xí)和掌握數(shù)據(jù)庫的用法,我們建議在開始學(xué)習(xí)后面的內(nèi)容之前,進(jìn)行如下設(shè)置。

應(yīng)用配置文件中開啟調(diào)試模式和頁面Trace,以及修改日志配置為errorsql日志單獨記錄(便于我們查錯和進(jìn)行SQL分析):

    // 開啟應(yīng)用調(diào)試模式
    'app_debug'              => true,
    // 開啟應(yīng)用Trace
    'app_trace'              => true,   
    'log'                    => [
        // 日志記錄方式
        'type'  => 'File',
        // error和sql日志單獨記錄
        'apart_level'   =>  ['error','sql'],
    ],

然后在數(shù)據(jù)庫配置文件開啟數(shù)據(jù)庫調(diào)試模式

    // 數(shù)據(jù)庫調(diào)試模式
    'debug'       => true,

開啟數(shù)據(jù)庫調(diào)試模式和頁面Trace的目的是為了便于在頁面直觀的看到當(dāng)前請求的數(shù)據(jù)庫連接情況和執(zhí)行的SQL語句,另外所有的歷史查詢SQL都可以在runtime\log\當(dāng)前日期\sql.log中查看,本書不打算給出每個查詢用法的最終SQL語句,最好的學(xué)習(xí)方式是親自驗證每個查詢的SQL語句是什么。

本書后面的示例代碼大多數(shù)情況不會寫完整的控制器文件和方法(這不是本書的重點),只是局部關(guān)鍵代碼的實現(xiàn),也假設(shè)你在調(diào)用Db類方法之前已經(jīng)使用use引入了think\Db類,請確保你已經(jīng)掌握控制器的用法以及如何訪問和測試(如果不清楚,請參考官方快速入門第三部控制器從入門到精通)。

創(chuàng)建查詢類

框架內(nèi)置的查詢類就是think\db\Query類,使用查詢構(gòu)造器一般都是自動實例化查詢類,無需手動實例化。通過第一章的學(xué)習(xí)我們已經(jīng)知道調(diào)用Db類的任何方法都會自動調(diào)用connect方法返回連接對象實例,然后調(diào)用連接對象的查詢構(gòu)造器方法會自動實例化查詢類。

以查詢構(gòu)造器的table方法調(diào)用為例,如果用下面方式調(diào)用:

// 調(diào)用Db類的table方法
Db::table('data');

實際解析過程相當(dāng)于調(diào)用連接類的table方法:

// 先實例化連接類然后調(diào)用table方法
Db::connect()->table('data');

然后繼續(xù)調(diào)用連接類的getQuery方法(該方法中會自動實例化查詢類)后再調(diào)用查詢類的table方法:

// 先實例化連接類然后獲取查詢類實例,調(diào)用查詢類的table方法
Db::connect()->getQuery()->table('data');

雖然實際的調(diào)用過程如上所示,但內(nèi)部的調(diào)用過程被數(shù)據(jù)訪問層封裝并銜接了,你只需要知道并使用下面的調(diào)用方法(除非你需要動態(tài)切換數(shù)據(jù)庫連接則需要調(diào)用connect方法先):

// 調(diào)用Db類的table方法
Db::table('data');

Db類可以直接調(diào)用查詢類的方法,數(shù)據(jù)訪問層的細(xì)分只是一種內(nèi)部的架構(gòu)設(shè)計,對于開發(fā)者來說,你只需要明白一件事:數(shù)據(jù)庫操作就是用Db類,至于內(nèi)部怎么相互調(diào)用完全不需要操心。

【5.1須知】


5.1 版本則簡化了調(diào)用流程,Db的靜態(tài)方法調(diào)用直接就是調(diào)用的查詢類的方法。也就是說,上面的方法其實就是調(diào)用了Query類的table方法,而沒有中間過程。

事實上,系統(tǒng)還額外提供了一個助手函數(shù)db用于完成上述方法,所以

$db = db('data');

和前面的用法等效,但是——有一點必須引起重視。

db助手函數(shù)默認(rèn)每次調(diào)用都會重新連接數(shù)據(jù)庫(目的是確保你的每次db函數(shù)調(diào)用不會相互影響),你可以使用db('data',[],false)方式解決。(好消息是V5.0.9+版本已經(jīng)完美解決,無需這樣調(diào)用了)

了解了查詢類的調(diào)用機(jī)制后,后面我們就要逐步來講解查詢類的各種查詢方法了,查詢器類內(nèi)置了大量的查詢方法用于構(gòu)建查詢,如果你仍然覺得不夠用,可以自己擴(kuò)展然后使用下面的方式配置調(diào)用。

如果你自定義或者擴(kuò)展了核心的查詢類,那么可以在數(shù)據(jù)庫配置文件中設(shè)置:

// 定義數(shù)據(jù)庫的查詢類
'query'           => '\\app\\db\\Query',

定義后,查詢構(gòu)造器就會自動調(diào)用app\db\Query查詢類(一般來說會繼承核心的think\db\Query類)。

數(shù)據(jù)庫CURD操作

使用查詢構(gòu)造器進(jìn)行查詢,起碼需要掌握查詢類的幾個關(guān)鍵的方法:

查詢方法 作用描述
table 指定查詢數(shù)據(jù)表
field 指定查詢字段
where 指定查詢條件
order 指定結(jié)果排序
limit 指定查詢結(jié)果數(shù)
find 查詢一條記錄
select 查詢數(shù)據(jù)集
insert 寫入數(shù)據(jù)
update 更新數(shù)據(jù)
delete 刪除數(shù)據(jù)

這些方法對SQL稍微了解一點的用戶理解起來應(yīng)該不難,findselect方法的區(qū)別在于find方法只是查詢一條記錄(即使?jié)M足條件的記錄有很多),并且返回的是一個一維數(shù)組(沒有結(jié)果返回Null),而select方法返回的是一個二維數(shù)組(沒有結(jié)果返回空數(shù)組),除此之外,他們的查詢語法都是相同的。

這些常用方法其實包含兩種大的類別,一個是輔助方法(輔助查詢用的,也稱為鏈?zhǔn)椒椒?,例?code>table、fieldwhere、orderlimit等方法),一個是真正的查詢方法(find、select、insert、updatedelete方法),查詢方法是必須的,而輔助方法是可選的,并且輔助方法必須在查詢方法之前被調(diào)用,并且在查詢調(diào)用之后自動失效。

【5.1須知】


5.1版本每次查詢完成后鏈?zhǔn)椒椒ǖ臈l件參數(shù)會保留。

關(guān)于table方法這里作出一個特別的說明,在新版框架的架構(gòu)設(shè)計規(guī)范中,我們建議數(shù)據(jù)表的命名不使用前綴設(shè)計,表前綴其實已經(jīng)是一種過時的設(shè)計了,很多時候跨庫的設(shè)計比表前綴的設(shè)計來的更靈活和實用,而且前綴設(shè)計(尤其是在混合用的情況下)帶來的一些困惑和問題卻是很多新手最大的苦惱,所以何必自尋煩惱(如果你一定要采用前綴設(shè)計,那么請用name方法替代table方法,并且在數(shù)據(jù)庫配置文件中配置prefix參數(shù),我也不攔著你,哭的時候別找我_)。

使用查詢構(gòu)造器進(jìn)行查詢,能夠最大程度的避免寫針對特定數(shù)據(jù)庫的查詢語句,減少跨數(shù)據(jù)庫類型的遷移成本。

下面我們對數(shù)據(jù)庫的CURD操作給出基本的用法。

創(chuàng)建(Create)

創(chuàng)建操作指往數(shù)據(jù)表添加新的記錄,下面是示例代碼:

// 插入單個記錄
Db::table('data')
    ->insert(['id' => 8, 'name' => 'thinkphp']);

// 插入多個記錄
Db::table('data')
    ->insertAll([
        ['id' => 9, 'name' => 'thinkphp'],
        ['id' => 10, 'name' => 'topthink'],
    ]);

insertAll方法的數(shù)據(jù)集中的元素請確保字段列表一致,否則會出錯。

由于insertinsertAll方法最終都是調(diào)用連接類的execute方法,我們已經(jīng)知道execute方法的返回值是影響的記錄數(shù),所以insertinsertAll方法的返回值也是影響(新增)的記錄數(shù),并不會返回主鍵值

主鍵id如果是自增類型,可以使用:

// 插入單個記錄
Db::table('data')
    ->insert(['name' => 'kancloud']);

如果需要獲取自增id的值,可以在insert方法之后緊接著調(diào)用getLastInsID方法:

// 插入單個記錄
Db::table('data')
    ->insert(['name' => 'kancloud']);
// 獲取上次寫入的自增Id
$id = Db::getLastInsID();

由于PDO內(nèi)部的原因,insertAll方法后調(diào)用getLastInsID方法返回的自增Id可能存在偏差。

或者直接合并上面的代碼為:

// 插入單個記錄 并返回自增Id
$id = Db::table('data')
    ->insertGetId(['name' => 'kancloud']);

對于不在數(shù)據(jù)表中的字段寫入,系統(tǒng)默認(rèn)會拋出異常,但可以配合strict(false)方法忽略錯誤繼續(xù)執(zhí)行,下面的test數(shù)據(jù)會被忽略(data表不存在test字段)。

// 插入單個記錄 并返回自增Id
$id = Db::table('data')
    ->strict(false)
    ->insertGetId([
        'name' => 'kancloud',
        'email' =>  'kancloud@qq.com',
        'test'  => '這個數(shù)據(jù)不會被寫入',
    ]);

你并不需要考慮寫入數(shù)據(jù)失敗的情況,數(shù)據(jù)庫操作過程有任何的錯誤都會拋出異常,你需要做的只是修正BUG或者捕獲異常自行處理。

更新(Update)

更新操作指更改數(shù)據(jù)表記錄的單個或者多個字段,下面是示例代碼:

// 更新記錄
Db::table('data')
    ->where('id', 8)
    ->update(['name' => "framework"]);

出于數(shù)據(jù)安全考慮,ThinkPHP的update方法必須使用更新條件而不允許無條件更新,如果沒有指定更新條件,則會從更新數(shù)據(jù)中獲取主鍵作為更新條件,例如當(dāng)id是主鍵的時候下面的寫法依然有效:

// 更新記錄
Db::table('data')
    ->update(['id' => 8, 'name' => "framework"]);

可以過濾需要更新的字段列表,例如只允許更新name字段的值(假設(shè)data表還存在email字段)

// 更新記錄
Db::table('data')
    ->field(['name'])
    ->where('id', 8)
    ->update([
            'name' => 'framework', 
            'email' => 'thinkphp@qq.com'
        ]);

實際更新的字段只有name,email字段的數(shù)據(jù)會被忽略。

一般來說,update方法用于更新數(shù)據(jù)的多個字段,如果只是更新某個字段的值,也可以用setField方法,例如:

// 更新記錄
Db::table('data')
    ->where('id', 8)
    ->setField('name','framework');

返回值和update方法一致,因為setField最終也是調(diào)用的update方法。

對于數(shù)字類型的字段的步長更新,框架提供了兩個專門的方法用于遞增和遞減操作。

遞增操作:

// score 字段加 1
Db::table('user')
    ->where('id', 1)
    ->setInc('score');
// score 字段加 5
Db::table('user')
    ->where('id', 1)
    ->setInc('score', 5);

遞減操作:

// score 字段減 1
Db::table('user')
    ->where('id', 1)
    ->setDec('score');
// score 字段減 5
Db::table('user')
    ->where('id', 1)
    ->setDec('score', 5);

setInc/setDec支持延時寫入,延時寫入的含義是會把需要遞增/遞減的數(shù)據(jù)緩存起來(在緩存中進(jìn)行遞增和遞減操作),在達(dá)到指定的時間計時后才會把最終計算的緩存數(shù)據(jù)寫入數(shù)據(jù)庫,避免頻繁操作數(shù)據(jù)庫帶來的性能開銷,下例中延時10秒,給score字段增加1:

Db::table('user')
    ->where('id', 1)
    ->setInc('score', 1, 10);

setIncsetDec可以同時使用延時寫入,系統(tǒng)會自動計算最終需要寫入數(shù)據(jù)庫的值。

讀?。≧ead)

讀取操作就是指對數(shù)據(jù)表(包括單表和多表)的各種查詢操作,該操作涉及的內(nèi)容和營養(yǎng)較多,也是數(shù)據(jù)庫操作中最復(fù)雜和最難掌握的,所以我們會陸續(xù)花大量的篇幅來講解,這里先簡單了解下幾個單表查詢最基礎(chǔ)的方法,用法示例:

查詢單個數(shù)據(jù):

// 查詢查個數(shù)據(jù)
$data = Db::table('data')
    ->where('id', 8)
    ->find();
dump($data);
// 簡化寫法,直接傳入主鍵查詢
$data = Db::table('data')
    ->find(8);

find方法存在查詢結(jié)果的話返回一個數(shù)組,沒有的話返回null,要獲取查詢記錄的值,可以使用數(shù)組方式操作:

echo $data['id'];
echo $data['name'];

查詢多個數(shù)據(jù):

// 查詢多個數(shù)據(jù)
$list = Db::table('data')
    ->where('id','in', [1, 5, 8])
    ->select();
dump($list);
// 直接傳入多個主鍵的值查詢
$list = Db::table('data')
    ->select([1,5,8]);

select方法存在查詢結(jié)果的話返回一個二維數(shù)組,如果沒有數(shù)據(jù)則返回一個空數(shù)組。

可以遍歷獲取記錄的值,例如:

foreach ($list as $data) {
    echo $data['name'];
}

我們修改數(shù)據(jù)庫配置文件中的resultset_type

    // 數(shù)據(jù)集返回類型
    'resultset_type'  => 'collection',

select方法的返回值就會變成一個數(shù)據(jù)集對象(think\Collection)。

二維數(shù)組和數(shù)據(jù)集對象的區(qū)別在于,數(shù)據(jù)集對象提供了更多的內(nèi)置數(shù)據(jù)處理方法,但在基本使用上,這兩種方式?jīng)]有不同,很多開發(fā)者一看到對象的輸出信息就不知所措了,完全沒必要,除非你有“對象”恐懼癥,關(guān)于數(shù)據(jù)集的用法我們會在后續(xù)進(jìn)行詳細(xì)講解。

一個特殊的情況是,使用分頁查詢方法的話,無論數(shù)據(jù)庫配置返回數(shù)據(jù)集類型設(shè)置什么,都會返回分頁類think\Paginator的對象實例,用法基本上和數(shù)據(jù)集對象一致。

5.1版本和5.0的一個關(guān)鍵的區(qū)別,在于5.1版本開始,每次查詢完成后不會清空查詢條件,但每次Db類的靜態(tài)調(diào)用都是一個全新的查詢。

獲取記錄的某個字段值:

// 返回某個字段的值
Db::table('data')
    ->where('id', 1)
    ->value('name');

獲取記錄某個列的值:

// 獲取name列的數(shù)組
Db::table('data')
    ->where('status', 1)
    ->column('name');

// 指定索引
Db::table('data')
    ->where('status', 1)
    ->column('name', 'id');

column的返回類型永遠(yuǎn)都是數(shù)組,不受resultset_type參數(shù)的影響。

你可能現(xiàn)在會對where方法的用法心存疑慮,確實查詢構(gòu)造器大部分的工作和復(fù)雜度都集中在where方法,不過不用著急,請耐心往下看,在學(xué)習(xí)完本章和下一章的內(nèi)容后你基本上就會對where方法駕輕就熟了。

刪除(Delete)

刪除操作指對數(shù)據(jù)表的單個記錄或者多個記錄的刪除操作,示例代碼如下:

// 刪除數(shù)據(jù)
Db::name('data')
    ->where('id', 18)
    ->delete();

和更新操作一樣,ThinkPHP不允許使用無條件刪除操作,如果不帶條件可以直接使用主鍵刪除:

// 刪除數(shù)據(jù)
Db::name('data')
    ->delete(18);
// 刪除多條數(shù)據(jù)
Db::name('data')
    ->delete([1, 5, 8]);

如果你確定要執(zhí)行無條件刪除操作, 可以使用下面的方式:

// 刪除所有數(shù)據(jù)
Db::name('data')
    ->delete(true);

事實上,對于業(yè)務(wù)數(shù)據(jù)表,基本上不建議使用刪除操作,而是使用軟刪除(邏輯刪除,其實執(zhí)行的是數(shù)據(jù)表的更新操作)替代實際的物理刪除。軟刪除屬于模型的功能設(shè)計,我們會在后面的章節(jié)給你講解。

使用鏈?zhǔn)椒椒?/h2>

掌握了基本的CURD操作后,我們就來熟悉下鏈?zhǔn)椒椒ǖ母拍?,其實就是前面我們提到的輔助查詢方法。首先鏈?zhǔn)椒椒ǖ哪康氖菫榱俗尣樵兏逦椭庇^,下面的兩個代碼實現(xiàn)哪個更清晰易懂大家可以比較下。

常規(guī)的方法實現(xiàn):

Db::table('data');
Db::where('id', '>', 1);
Db::limit(8);
$list = Db::select();

事實上,如果在5.1版本中,上面的用法是無效的。

使用鏈?zhǔn)椒椒▽崿F(xiàn):

$list = Db::table('data')
    ->where('id', '>', 1)
    ->limit(8)
    ->select();

事實上,我們前面使用的table、where、limit之類的方法都稱之為鏈?zhǔn)椒椒ǎ鎰e一個方法是否屬于鏈?zhǔn)椒椒ǖ囊粋€顯著特征就是看這個方法是否返回當(dāng)前的對象實例。

正因為鏈?zhǔn)讲僮鞣椒ǚ祷氐氖钱?dāng)前對象實例,所以不同的鏈?zhǔn)椒椒ㄔ谡{(diào)用的順序上沒有先后的概念,但相同的鏈?zhǔn)椒椒ㄕ{(diào)用順序會影響最終的查詢,下面的兩個例子完全等效:

$list = Db::table('data')
    ->where('id', '>', 1)
    ->limit(8)
    ->select();

$list = Db::limit(8)
    ->where('id', '>', 1)
    ->table('data')
    ->select();

上面兩個查詢最終生成的SQL語句是完全相同的。

但下面兩個查詢最終生成的SQL是不同的

$list = Db::table('data')
    ->where('id', '>', 1)
    ->where('name', 'like', '%think%')
    ->order('id', 'desc')
    ->order('create_time', 'desc')
    ->limit(8)
    ->select();

$list = Db::table('data')
    ->where('name', 'like', '%think%')
    ->where('id', '>', 1)
    ->order('create_time', 'desc')
    ->order('id', 'desc')
    ->limit(8)
    ->select();

多個where方法和order方法的調(diào)用順序最終影響了生成的SQL語句,雖然有時候并不會影響查詢結(jié)果。上面的兩個例子where方法的順序并不會影響查詢條件,而order方法的順序則改變了最終數(shù)據(jù)的排序。

鏈?zhǔn)椒椒ǖ恼{(diào)用順序取決于你的思維習(xí)慣或者說團(tuán)隊規(guī)范。

查詢類的大部分方法都是采用鏈?zhǔn)椒椒▽崿F(xiàn),給你的CURD查詢帶來便利。

查詢類的所有鏈?zhǔn)椒椒ㄕ{(diào)用都不具備記憶性,每次查詢操作完成后幾乎所有的鏈?zhǔn)椒椒◣淼挠绊憣⒉粡?fù)存在,簡單來說就是鏈?zhǔn)椒椒ǖ慕Y(jié)果不會帶入后面的其它查詢。

系統(tǒng)支持的鏈?zhǔn)讲僮鞣椒ㄓ校?/p>

連貫操作 作用 支持的參數(shù)類型
where* 用于AND查詢 字符串、數(shù)組和對象
whereOr* 用于OR查詢 字符串、數(shù)組和對象
whereXor* 用于XOR查詢 字符串、數(shù)組和對象
whereTime* 用于時間日期的快捷查詢 字符串
table 用于定義要操作的數(shù)據(jù)表名稱 字符串和數(shù)組
alias 用于給當(dāng)前數(shù)據(jù)表定義別名 字符串和數(shù)組
field* 用于定義要查詢的字段(支持字段排除) 字符串和數(shù)組
order* 用于對結(jié)果排序 字符串和數(shù)組
limit 用于限制查詢結(jié)果數(shù)量 字符串和數(shù)字
page 用于查詢分頁(內(nèi)部會轉(zhuǎn)換成limit) 字符串和數(shù)字
group 用于對查詢的group支持 字符串
having 用于對查詢的having支持 字符串
join* 用于對查詢的join支持 字符串和數(shù)組
union* 用于對查詢的union支持 字符串、數(shù)組和對象
view* 用于視圖查詢 字符串、數(shù)組
distinct 用于查詢的distinct支持 布爾值
lock 用于數(shù)據(jù)庫的鎖機(jī)制 布爾值
cache 用于查詢緩存 支持多個參數(shù)
relation* 用于關(guān)聯(lián)查詢 字符串
with* 用于關(guān)聯(lián)預(yù)載入 字符串、數(shù)組
bind* 用于數(shù)據(jù)綁定操作 數(shù)組或多個參數(shù)
comment 用于SQL注釋 字符串
force 用于數(shù)據(jù)集的強(qiáng)制索引 字符串
master 用于設(shè)置主服務(wù)器讀取數(shù)據(jù) 布爾值
strict 用于設(shè)置是否嚴(yán)格檢測字段名是否存在 布爾值
sequence 用于設(shè)置Pgsql的自增序列名 字符串
failException 用于設(shè)置沒有查詢到數(shù)據(jù)是否拋出異常 布爾值
partition 用于設(shè)置分表信息 數(shù)組 字符串
data* 用于設(shè)置寫入數(shù)據(jù)(5.0.5+ 數(shù)組
inc* 用于設(shè)置字段遞增 (5.0.5+ 字符串
dec* 用于設(shè)置字段遞減(5.0.5+ 字符串
exp* 用于設(shè)置SQL表達(dá)式(5.0.5+ 字符串

其中帶*標(biāo)識的表示支持多次調(diào)用。

鑒于鏈?zhǔn)椒椒ㄓ泻芏?,而且手冊也有詳?xì)的描述(完全開發(fā)手冊對鏈?zhǔn)椒椒?/a>給出了詳細(xì)的解釋,可以參閱),這里想聲明幾點,避免產(chǎn)生困惑:

  • 鏈?zhǔn)椒椒ㄖС炙械腃URD操作;
  • 鏈?zhǔn)椒椒ū旧碇皇欠祷夭樵儗ο?,只有?zhí)行查詢后才會返回結(jié)果,而且只能在查詢方法之前被調(diào)用;
  • 不同鏈?zhǔn)椒椒ǖ恼{(diào)用順序不影響查詢;
  • 相同鏈?zhǔn)椒椒ǖ恼{(diào)用順序可能會影響查詢(至少會影響SQL語句)
  • 鏈?zhǔn)椒椒ㄔ谕瓿刹樵兒髸詣邮В?/li>
  • 同一個鏈?zhǔn)椒椒ㄔ贑URD操作中的作用可能不同;
  • 鏈?zhǔn)椒椒▋H針對CURD方法,對原生查詢無效;

一個典型的例子就是field方法,在查詢和寫入操作中的代表的作用完全不同。

查詢語言

在所有的鏈?zhǔn)椒椒ㄖ?,最?fù)雜的莫過于查詢條件方法以及和其它方法的配合。和查詢條件相關(guān)的用法我們稱之為查詢語言,查詢語言可以用于數(shù)據(jù)庫的URD操作,要掌握查詢語言的核心,謹(jǐn)記:2個方法,3個用法,8個要訣。

2個方法

其中兩個方法是:

方法 描述
where AND條件查詢
whereOr OR條件查詢

上面兩個方法以及和其它的鏈?zhǔn)椒椒ㄅ浜峡梢越M合出滿足需求的所有條件查詢語法,wherewhereOr方法可以在一次查詢操作中多次調(diào)用,并且在調(diào)用Query類的find/select/update/delete方法(及其衍生方法)的時候,由Builder類最終生成一個包含查詢條件的SQL語句。

下面是一個測試查詢代碼:

Db::table('data')
    ->where('name', 'like', '%thinkphp')
    ->whereOr('id', '>', 1)
    ->find();

和下面的查詢結(jié)果可以自行比較下,然后揣測下wherewhereOr方法的區(qū)別以及調(diào)用順序的影響:

Db::table('data')
    ->where('id', '>', 1)
    ->whereOr('name', 'like', '%thinkphp')
    ->find();

3個用法

3個用法就是表達(dá)式、數(shù)組閉包用法,并且是支持混合使用的。

要掌握查詢語言的用法,必須在上面兩個方法和兩個用法的基礎(chǔ)上多嘗試,并且及時在頁面Trace中查看最終生成的SQL語句是否是滿足實際查詢需求(或者說是期望的查詢條件),這才是掌握查詢的不二法門。

先來說表達(dá)式用法,這是查詢語言的基礎(chǔ),查詢方法(包括wherewhereOr,這里以where為例)參數(shù)用法為:

where('字段名','表達(dá)式','查詢條件')

[info]#### where('字段名','查詢條件(等于)')
[info]#### where('字符串查詢條件','參數(shù)綁定(數(shù)組)')

表達(dá)式不分大小寫,支持的查詢表達(dá)式有下面幾種,分別表示的含義是:

表達(dá)式 含義 查詢條件類型
= 等于 字符串或者數(shù)字
<> 不等于 字符串或者數(shù)字
> 大于 字符串或者數(shù)字
>= 大于等于 字符串或者數(shù)字
< 小于 字符串或者數(shù)字
<= 小于等于 字符串或者數(shù)字
[not] like 模糊查詢 字符串
[not] between (不在)區(qū)間查詢 字符串或者數(shù)組
[not] in (不在)IN 查詢 字符串、數(shù)組或閉包
[not] null 查詢字段是否(不)是NULL
[not] exists EXISTS查詢 字符串或者閉包
exp 表達(dá)式查詢,支持SQL語法 字符串
> time 時間比較 字符串或者數(shù)字
< time 時間比較 字符串或者數(shù)字
>= time 時間比較 字符串或者數(shù)字
<= time 時間比較 字符串或者數(shù)字
between time 時間比較 字符串或者數(shù)字
notbetween time 時間比較 字符串或者數(shù)字

測試代碼如下:

Db::table('data')
    ->where('id', '<>', 8)
    ->where('id', 'between', [1, 20])
    ->whereOr('name', 'like', '%thinkphp')
    ->select();

關(guān)于表達(dá)式查詢語法這里補(bǔ)充說明下,下面的幾種都是等效的:

表達(dá)式用法一 表達(dá)式用法二
not like notlike
not null notnull
not in notin
not between notbetween
not exists notexists
not between time notbetween time

所有帶between的表達(dá)式,查詢條件必須包含兩個元素(或者用逗號分隔的兩個元素),否則會拋出異常,所以在傳入查詢條件之前最好先驗證下。

數(shù)組用法其實是多字段的表達(dá)式用法,在一個方法完成所有的查詢條件,用法如下:

where([

'字段名1' => ['表達(dá)式', '查詢條件'],
'字段名2' => ['表達(dá)式', '查詢條件'],
'字段名2' => '條件(等于)',
...
])

測試代碼:

Db::table('data')
    ->where(['name' => ['like', '%thinkphp'], 'id' => ['>', 1]])
    ->whereOr(['id' => ['<', 10], 'name' => ['like', '%php%']])
    ->find();

5.1須知


如果是5.1版本,數(shù)組查詢條件必須改成如下用法:

Db::table('data')
    ->where([ 
      ['name', 'like', '%thinkphp'], 
        ['id', '>', 1]
    ])
    ->whereOr([ 
      ['id', '<', 10], 
        ['name', 'like', '%php%']
    ])
    ->find();

數(shù)組用法不夠靈活,有時候需要和其它用法配合使用,出于安全考慮也并不推薦。

閉包用法指的是直接在where或者whereOr方法中傳入閉包,和前面兩種用法配合可以完成復(fù)雜的查詢條件,例如:

$result = Db::table('data')
    ->where(function ($query) {
        $query->where('id', 1)->whereOr('id', '>', 2);
    })
    ->whereOr(function ($query) {
        $query->where('name', 'like', '%think%')->where('id', '<>', 8);
    })
    ->select();

閉包方法只有一個查詢對象參數(shù),如果需要在閉包中使用外部的變量,可以使用閉包的use語法,例如:

$id     = 1;
$name   = 'think';
$result = Db::table('data')
    ->where(function ($query) use ($id) {
        $query->where('id', $id);
    })
    ->whereOr(function ($query) use ($id, $name) {
        $query->where('name', 'like', '%' . $name . '%')->where('id', '<>', $id);
    })
    ->select();

8個要訣:

在使用查詢的過程中謹(jǐn)記下面8個要訣,幫助你更好的完成查詢。

  • 查詢條件的調(diào)用次序就是生成SQL的條件順序;
  • 查詢字段用&分割表示對多個字段使用AND查詢;
  • 查詢字段用|分割表示對多個字段使用OR查詢;
  • 對同一個查詢字段多次調(diào)用非等查詢條件會合并查詢;
  • 閉包查詢和EXP查詢會在生成的查詢語句兩邊加上括號;
  • 用閉包查詢替代3.2版本的組合查詢;
  • 除了EXP查詢外,其它查詢都會自動使用參數(shù)綁定;
  • 如果查詢條件來自用戶輸入,盡量使用表達(dá)式和閉包查詢,數(shù)組條件查詢務(wù)必使用官方推薦的方法獲取變量;

總結(jié)

本章我們學(xué)習(xí)了數(shù)據(jù)庫的基礎(chǔ)查詢用法,以及如何使用鏈?zhǔn)椒椒ㄍ瓿刹樵儤?gòu)造器。下一章我們會來學(xué)習(xí)下更多的高級查詢用法。

上一篇:第二章:數(shù)據(jù)創(chuàng)建和遷移
下一篇:第四章:高級查詢技巧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,436評論 2 378

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