ThinkPHP5 閉包查詢

看了一下源碼(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)如下兩段:

Query的select方法,$query使用當前實例的引用


Builder處理where閉包的地方,$query使用當前連接實例化的新對象

? ? ? 如果下面處理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類:


Query類的select操作中生成SQL的步驟,這個select()并非我們在代碼里寫的select(),注意咯

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

繼承于Builder類的數(shù)據(jù)庫驅(qū)動類

? ? ? 因此在處理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閉包之間沒有關系。

Builder類生成union閉包SQL的地方

Query類生成SQL的方法

多個union閉包
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容