看了一下源碼(Query.php)之后,總結(jié)一下簸喂,所有的查詢執(zhí)行方法(select,find等),在傳入一個(gè)匿名函數(shù)進(jìn)行閉包查詢時(shí)燎潮,需要傳入一個(gè)參數(shù)(名字隨意),在源碼中喻鳄,該參數(shù)為當(dāng)前數(shù)據(jù)庫(kù)連接實(shí)例本身的引用(&this),即在匿名函數(shù)內(nèi)所有的條件操作(where,order等)确封,本質(zhì)上都是給本次查詢實(shí)例添加查詢條件除呵,與基本查詢的鏈?zhǔn)讲僮鳑]有本質(zhì)區(qū)別,好處是爪喘,在閉包內(nèi)進(jìn)行操作颜曾,可以創(chuàng)建一個(gè)封閉的作用域,一定程度上解決可能出現(xiàn)的變量沖突問題秉剑,關(guān)于如何將外部的查詢條件導(dǎo)入到閉包內(nèi)部泛豪,可以使用function()use(){},在use()中導(dǎo)入外部參數(shù)。
? ? ? ? 在閉包查詢的時(shí)候诡曙,至少要指定一次查詢表名臀叙,但不限位置,
可以:
\think\Db::select(function($query){
? ? $query->name('tableName')->where();
});
也可以:
\think\Db::name('tableName')->select(function($query){
? ? $query->where();
});
可以在閉包內(nèi)對(duì)不同實(shí)例進(jìn)行閉包查詢价卤,但請(qǐng)勿在閉包內(nèi)多次對(duì)同一實(shí)例嵌套使用閉包查詢劝萤,因?yàn)樵谠创a中,在處理完匿名函數(shù)之后荠雕,只是清空了查詢條件稳其,并沒有終止本次查詢,因此在對(duì)同一實(shí)例進(jìn)行嵌套閉包查詢時(shí)炸卑,在所有嵌套閉包中添加的查詢條件雖然都會(huì)生效既鞠,但是僅有最內(nèi)層閉包可以根據(jù)嵌套中添加的條件進(jìn)行查詢,在最內(nèi)層查詢結(jié)束之后盖文,將清空查詢條件($data = null)嘱蛋,并且不會(huì)終止查詢,因此會(huì)根據(jù)對(duì)應(yīng)方法(find,select等)的默認(rèn)條件五续,由內(nèi)至外洒敏,逐層進(jìn)行查詢,直到單個(gè)實(shí)例所有嵌套的閉包查詢完畢疙驾,其中凶伙,僅有最內(nèi)層查詢集合了嵌套查詢中所有的條件。
2017/07/04 14:59補(bǔ)充
? ? 看了一下混合查詢部分它碎,個(gè)人感覺這個(gè)TP5的閉包查詢就是一個(gè)比較晦澀的鏈?zhǔn)讲僮骱伲徊贿^把實(shí)例變成變成了一個(gè)參數(shù)的形式,除此之外就對(duì)這個(gè)參數(shù)進(jìn)行普通的鏈?zhǔn)讲僮骷纯砂飧兀琫mmmm…
? ? ? 仔細(xì)研究了一下where方法對(duì)閉包條件的處理傻挂,他是在Builder.php里面對(duì)閉包方法進(jìn)行處理的,思路和select方法的處理思路相同挖息,在地258行開始金拒,判斷是否閉包條件,如果是就去調(diào)用閉包套腹,并傳入一個(gè)query類實(shí)例作為操作載體绪抛,回環(huán)調(diào)用直到處理完所有嵌套閉包。
使用思路差不多电禀,單純和select的閉包一樣用就行了睦疫,只是TP在處理的時(shí)候有細(xì)節(jié)上的差異。
2017/07/04 20:01補(bǔ)充
? ? ? 為什么這樣的寫法無效鞭呕?
因?yàn)樵赒uery類的where方法中,where在針對(duì)閉包函數(shù)的處理方式并非像select這種方法一樣立刻對(duì)閉包進(jìn)行處理,而是把閉包扔進(jìn)$this->options['where']里面做為一個(gè)待處理的對(duì)象放在那里葫松,因此閉包內(nèi)容并不會(huì)立即執(zhí)行瓦糕,所以閉包內(nèi)的find()其實(shí)是沒用的,換句話說里面怎么嵌套查詢語句都沒用,必須要在最外層手動(dòng)調(diào)用一次查詢操作腋么,在調(diào)用到查詢方法的時(shí)候咕娄,才會(huì)去處理where閉包里面的內(nèi)容,生成sql語句(詳情見Builder.php)珊擂。
2017/07/04 21:36補(bǔ)充
? ? ? 在測(cè)試中發(fā)現(xiàn)如果在where()中使用閉包修改表名似乎是無效的圣勒,還是會(huì)根據(jù)初始的表名來查詢;而在select()之類的查詢方法中使用name()修改表名則有效摧扇,在代碼中發(fā)現(xiàn)如下兩段:
? ? ? 如果下面處理where閉包的$query非select的$query扛稽,那么又是如何將條件成功注入到查詢中去的呢吁峻?
談一下我的理解:
? ? ? 這里實(shí)例化一個(gè)新的Query類的作用,并不是和select那樣做一個(gè)怎么樣的處理在张,只是為了照顧到語法的便捷性用含,為了讓里面和select閉包一樣寫的方法能夠成功調(diào)用,如where帮匾,join等方法啄骇,本身就是存在于Query類中的,而非處理where閉包的Builder類中。
為什么不用this:
? ? 上面說了瘟斜,處理where閉包的(Builder)類和處理select閉包的(Query)類是兩個(gè)類缸夹,在Query類的select方法中,生成查詢sql的方法其實(shí)調(diào)用的是Builder類:
$this->builder正是繼承于Builder類的數(shù)據(jù)庫(kù)驅(qū)動(dòng)類Mysql:
? ? ? 因此在處理where閉包的時(shí)候使用this,那么這個(gè)this并非Query類的實(shí)例,而是繼承于Builder的Mysql驅(qū)動(dòng)類的實(shí)例壹蔓,然而這個(gè)類里并沒有操作數(shù)據(jù)庫(kù)的方法(在Query類里面)趟妥,所以只能直接new一個(gè)新的Query類來處理where閉包里面的操作。
直接new一個(gè)Query類為什么還能查到正確的sql結(jié)果:
? ? ? 因?yàn)榈竭@里都是生成SQL查詢語句了佣蓉,返回的直接就是個(gè)拿去用的SQL字符串披摄,到底給誰用,不是在這里確定的勇凭,所以這個(gè)方法只要能夠正確的解析出where的閉包條件就算搞定收工疚膊,在SQL語句里面,查什么表也不是在where里面確定的說虾标,唯一不方便的寓盗,就是和select閉包相比,無法改變和設(shè)置查詢的表名,還是讓人有點(diǎn)糾結(jié)的說傀蚌。
為什么不把Query實(shí)例的的$this引用到Builder類作$query:
? ? ? 因?yàn)镼uery實(shí)例中基显,使用本身保存的Builder的實(shí)例,在調(diào)用時(shí)如同在一個(gè)類里面自己引用自己,然而PHP類實(shí)例不能自己引用自己善炫。好糾結(jié)的玩意......
2017/07/05 08:57補(bǔ)充
? ? ? ? 以上只是說明where閉包和select閉包的一點(diǎn)差異:select可以用name改表而where閉包不能撩幽。
2017/07/05 10:39補(bǔ)充
? ? ? union操作閉包,這個(gè)操作的閉包必須單獨(dú)設(shè)置一個(gè)name或table箩艺,這里根select和where都不一樣窜醉。處理這個(gè)閉包的地方和where一樣,在Builder類里面艺谆,也是實(shí)例化一個(gè)新的Query類來處理榨惰,和where生成SQL語句不同的是,where類生成的SQL語句只是查詢語句的where部分擂涛,這個(gè)是用Builder類的builderWhere方法(嵌套時(shí)回環(huán)調(diào)用)來生成的读串,而union在生成的查詢SQL中是一個(gè)子查詢,而子查詢是獨(dú)立與主查詢的撒妈,因此需要在每個(gè)union閉包中設(shè)置自己的name或table恢暖,在代碼中,雖然union閉包也是在Builder類里去處理的狰右,但是不是調(diào)用的Builder類的方法生成子查詢杰捂,而是調(diào)用的該閉包中Query實(shí)例的select(false)去生成的子查詢,union閉包需要查詢那張表棋蚌,必須在union閉包里寫出來嫁佳。在處理union閉包的時(shí)候,使用的是foreach循環(huán)谷暮,如果有多個(gè)union閉包蒿往,則每個(gè)閉包都需要寫name或table,因?yàn)槊看窝h(huán)中處理union閉包時(shí)都會(huì)創(chuàng)建一個(gè)新的Query實(shí)例來生成子查詢湿弦,每個(gè)union閉包之間沒有關(guān)系瓤漏。