看了一下源碼(Query.php)之后,總結(jié)一下,所有的查詢執(zhí)行方法(select,find等),在傳入一個匿名函數(shù)進行閉包查詢時,需要傳入一個參數(shù)(名字隨意),在源碼中,該參數(shù)為當前數(shù)據(jù)庫連接實例本身的引用(&this),即在匿名函數(shù)內(nèi)所有的條件操作(where,order等),本質(zhì)上都是給本次查詢實例添加查詢條件,與基本查詢的鏈式操作沒有本質(zhì)區(qū)別,好處是,在閉包內(nèi)進行操作,可以創(chuàng)建一個封閉的作用域,一定程度上解決可能出現(xiàn)的變量沖突問題,關于如何將外部的查詢條件導入到閉包內(nèi)部,可以使用function()use(){},在use()中導入外部參數(shù)。
? ? ? ? 在閉包查詢的時候,至少要指定一次查詢表名,但不限位置,
可以:
\think\Db::select(function($query){
? ? $query->name('tableName')->where();
});
也可以:
\think\Db::name('tableName')->select(function($query){
? ? $query->where();
});
可以在閉包內(nèi)對不同實例進行閉包查詢,但請勿在閉包內(nèi)多次對同一實例嵌套使用閉包查詢,因為在源碼中,在處理完匿名函數(shù)之后,只是清空了查詢條件,并沒有終止本次查詢,因此在對同一實例進行嵌套閉包查詢時,在所有嵌套閉包中添加的查詢條件雖然都會生效,但是僅有最內(nèi)層閉包可以根據(jù)嵌套中添加的條件進行查詢,在最內(nèi)層查詢結(jié)束之后,將清空查詢條件($data = null),并且不會終止查詢,因此會根據(jù)對應方法(find,select等)的默認條件,由內(nèi)至外,逐層進行查詢,直到單個實例所有嵌套的閉包查詢完畢,其中,僅有最內(nèi)層查詢集合了嵌套查詢中所有的條件。
2017/07/04 14:59補充
? ? 看了一下混合查詢部分,個人感覺這個TP5的閉包查詢就是一個比較晦澀的鏈式操作,只不過把實例變成變成了一個參數(shù)的形式,除此之外就對這個參數(shù)進行普通的鏈式操作即可,emmmm…
? ? ? 仔細研究了一下where方法對閉包條件的處理,他是在Builder.php里面對閉包方法進行處理的,思路和select方法的處理思路相同,在地258行開始,判斷是否閉包條件,如果是就去調(diào)用閉包,并傳入一個query類實例作為操作載體,回環(huán)調(diào)用直到處理完所有嵌套閉包。
使用思路差不多,單純和select的閉包一樣用就行了,只是TP在處理的時候有細節(jié)上的差異。
2017/07/04 20:01補充
? ? ? 為什么這樣的寫法無效?

因為在Query類的where方法中,where在針對閉包函數(shù)的處理方式并非像select這種方法一樣立刻對閉包進行處理,而是把閉包扔進$this->options['where']里面做為一個待處理的對象放在那里,因此閉包內(nèi)容并不會立即執(zhí)行,所以閉包內(nèi)的find()其實是沒用的,換句話說里面怎么嵌套查詢語句都沒用,必須要在最外層手動調(diào)用一次查詢操作,在調(diào)用到查詢方法的時候,才會去處理where閉包里面的內(nèi)容,生成sql語句(詳情見Builder.php)。
2017/07/04 21:36補充
? ? ? 在測試中發(fā)現(xiàn)如果在where()中使用閉包修改表名似乎是無效的,還是會根據(jù)初始的表名來查詢;而在select()之類的查詢方法中使用name()修改表名則有效,在代碼中發(fā)現(xiàn)如下兩段:


? ? ? 如果下面處理where閉包的$query非select的$query,那么又是如何將條件成功注入到查詢中去的呢?
談一下我的理解:
? ? ? 這里實例化一個新的Query類的作用,并不是和select那樣做一個怎么樣的處理,只是為了照顧到語法的便捷性,為了讓里面和select閉包一樣寫的方法能夠成功調(diào)用,如where,join等方法,本身就是存在于Query類中的,而非處理where閉包的Builder類中。
為什么不用this:
? ? 上面說了,處理where閉包的(Builder)類和處理select閉包的(Query)類是兩個類,在Query類的select方法中,生成查詢sql的方法其實調(diào)用的是Builder類:

$this->builder正是繼承于Builder類的數(shù)據(jù)庫驅(qū)動類Mysql:

? ? ? 因此在處理where閉包的時候使用this,那么這個this并非Query類的實例,而是繼承于Builder的Mysql驅(qū)動類的實例,然而這個類里并沒有操作數(shù)據(jù)庫的方法(在Query類里面),所以只能直接new一個新的Query類來處理where閉包里面的操作。
直接new一個Query類為什么還能查到正確的sql結(jié)果:
? ? ? 因為到這里都是生成SQL查詢語句了,返回的直接就是個拿去用的SQL字符串,到底給誰用,不是在這里確定的,所以這個方法只要能夠正確的解析出where的閉包條件就算搞定收工,在SQL語句里面,查什么表也不是在where里面確定的說,唯一不方便的,就是和select閉包相比,無法改變和設置查詢的表名,還是讓人有點糾結(jié)的說。
為什么不把Query實例的的$this引用到Builder類作$query:
? ? ? 因為Query實例中,使用本身保存的Builder的實例,在調(diào)用時如同在一個類里面自己引用自己,然而PHP類實例不能自己引用自己。好糾結(jié)的玩意......
2017/07/05 08:57補充
? ? ? ? 以上只是說明where閉包和select閉包的一點差異:select可以用name改表而where閉包不能。
2017/07/05 10:39補充
? ? ? union操作閉包,這個操作的閉包必須單獨設置一個name或table,這里根select和where都不一樣。處理這個閉包的地方和where一樣,在Builder類里面,也是實例化一個新的Query類來處理,和where生成SQL語句不同的是,where類生成的SQL語句只是查詢語句的where部分,這個是用Builder類的builderWhere方法(嵌套時回環(huán)調(diào)用)來生成的,而union在生成的查詢SQL中是一個子查詢,而子查詢是獨立與主查詢的,因此需要在每個union閉包中設置自己的name或table,在代碼中,雖然union閉包也是在Builder類里去處理的,但是不是調(diào)用的Builder類的方法生成子查詢,而是調(diào)用的該閉包中Query實例的select(false)去生成的子查詢,union閉包需要查詢那張表,必須在union閉包里寫出來。在處理union閉包的時候,使用的是foreach循環(huán),如果有多個union閉包,則每個閉包都需要寫name或table,因為每次循環(huán)中處理union閉包時都會創(chuàng)建一個新的Query實例來生成子查詢,每個union閉包之間沒有關系。


