一、Db類庫(kù)巧妙結(jié)合連接器、查詢器、sql生成器使用
在上目錄中咔咔使用了query作為案例演示,這個(gè)使用在框架中是不建議使用的,因?yàn)樵诰S護(hù)的方面會(huì)有一定的難度。
本節(jié)案例將會(huì)使用框架常用的查詢數(shù)據(jù)庫(kù)方式進(jìn)行查詢。
在上圖中可以看到使用了平時(shí)最常用的查詢方式,接下來(lái)將會(huì)對(duì)這組案例進(jìn)行詳細(xì)分析。
同樣代碼會(huì)來(lái)到Db類的__callStatic這個(gè)方法,這個(gè)方法就是在調(diào)用沒有聲明的靜態(tài)方法會(huì)進(jìn)行執(zhí)行的。
這個(gè)方法跟__call方法是有區(qū)別的,__call方法是調(diào)用不存在的方法會(huì)進(jìn)行調(diào)用,一定要注意倆者的區(qū)別。
對(duì)于上圖方法中static::connect()執(zhí)行最后會(huì)返回?object(thinkdbQuery)這個(gè)對(duì)象,至于內(nèi)部流程的執(zhí)行可以參考第二目錄的內(nèi)容。
所以執(zhí)行流程會(huì)來(lái)到thinkphp/library/think/db/Query.php這個(gè)類的table方法。
參數(shù)就是table中傳遞的數(shù)據(jù)庫(kù)表名tp_test。
按照上圖提供的代碼會(huì)對(duì)傳遞過(guò)來(lái)的表名進(jìn)行三次判斷。
-
第一次判斷是否為字符串 -
第二次判斷是否存在 ) -
第三次判斷是否存在 ,
根據(jù)傳遞過(guò)來(lái)的字符串以上三個(gè)判斷均不成立,于是會(huì)執(zhí)行到下面流程。
在table這個(gè)方法中可以看到最后的執(zhí)行流程就是將傳遞過(guò)來(lái)的表名存放在屬性options這個(gè)里邊。
并且最后會(huì)將thinkdbQuery Object這個(gè)對(duì)象進(jìn)行返回。
立即學(xué)習(xí)“PHP免費(fèi)學(xué)習(xí)筆記(深入)”;
where方法解析
table方法分析完成后會(huì)緊接著執(zhí)行where方法,同樣還是在類thinkphp/library/think/db/Query.php
上圖中在這個(gè)類中可以看到一個(gè)方法func_get_args,這個(gè)方法會(huì)返回一個(gè)包含函數(shù)參數(shù)列表的數(shù)組。
這個(gè)方法平時(shí)都是跟call_user_func_array同時(shí)使用,之前咔咔也使用這倆個(gè)方法進(jìn)行過(guò)一次案例實(shí)驗(yàn)。
然后會(huì)使用函數(shù)array_shift刪除數(shù)組中的第一個(gè)元素(red),并返回被刪除元素的值。
下圖第一個(gè)結(jié)果為func_get_args這個(gè)方法獲取出來(lái)的數(shù)據(jù),第二組結(jié)果為array_shift這個(gè)方法返回的結(jié)果。
倆組結(jié)果返回的值可以進(jìn)行對(duì)比一下,可以更好的理解array_shift的使用場(chǎng)景。
緊接著會(huì)進(jìn)行分析查詢表達(dá)式,也就是方法parseWhereExp做的事情。
在這個(gè)方法中需要注意一個(gè)點(diǎn)就是關(guān)于傳遞過(guò)來(lái)的這倆個(gè)參數(shù)。
參數(shù)一為查詢邏輯,參數(shù)二就是在使用案例時(shí)傳入的參數(shù)。
在代碼的第一行就需要我們來(lái)學(xué)習(xí)的一個(gè)知識(shí)點(diǎn)instanceof。
instanceof可以判斷某個(gè)對(duì)象是否是某個(gè)類的實(shí)例,判斷一個(gè)對(duì)象是否實(shí)現(xiàn)了某個(gè)接口。
關(guān)于這個(gè)的使用案例在文章ThinkPHP源碼解析之控制器這一文中做了詳細(xì)的說(shuō)明。
根據(jù)學(xué)習(xí)instanceof的作用可以清晰的明白第一個(gè)判斷不會(huì)進(jìn)行執(zhí)行。
在繼續(xù)學(xué)習(xí)以下的執(zhí)行流程,根據(jù)咔咔圈出來(lái)的框來(lái)進(jìn)行對(duì)代碼進(jìn)行簡(jiǎn)單的解析。
根據(jù)上圖首先會(huì)對(duì)查詢邏輯的符號(hào)全部轉(zhuǎn)為小寫
然后在進(jìn)行判斷$field instanceof Where傳遞過(guò)來(lái)的參數(shù)是否為Where類的實(shí)例。
最后一個(gè)判斷就是$field instanceof Expression跟上一步是判斷同樣的功能。
所以說(shuō)代碼最終的執(zhí)行邏輯就是下圖圈到的部分。
還記得在案例過(guò)程中給where傳遞的參數(shù)就是一個(gè)數(shù)組。
如果將參數(shù)改為where(‘t_id’,1)則就會(huì)走is_string($field)的這個(gè)流程,這個(gè)流程就交給大家了,咔咔就不去解析。
這里咔咔還是使用數(shù)組作為參數(shù)進(jìn)行解析,那么代碼依然會(huì)執(zhí)行本類的parseArrayWhereItems這個(gè)方法
在這個(gè)方法中先需要知道key會(huì)返回什么,從當(dāng)前內(nèi)部指針位置返回元素鍵名。
所以代碼會(huì)去執(zhí)行if語(yǔ)句的判斷,根據(jù)上邊的所有判斷都不符合所以會(huì)執(zhí)行這段代碼$where[] = [$key, is_array($val) ? ‘IN’ : ‘=’, $val];
這段代碼會(huì)判斷循環(huán)數(shù)組的value值是否為數(shù)組,如果為數(shù)組就是in,反之為=,由于value為1所以數(shù)組的第二個(gè)值為=。
那么最終where的值就是下圖打印的數(shù)據(jù)。
由于where不為空,代碼執(zhí)行流程會(huì)執(zhí)行到下圖位置,最終在返回本類實(shí)例。
find()執(zhí)行流程
接著代碼會(huì)還是執(zhí)行本類的find方法,查找單條記錄。
由于find中是沒有傳遞參數(shù)的,所以代碼會(huì)執(zhí)行到$this->parseOptions();分析表達(dá)式(可用于查詢或者寫入操作)
就目前寫的案例而言,這段看似很長(zhǎng)的代碼大家好好看看都可以看明白,最終依然是返回當(dāng)前的所有參數(shù)。
以下就是返回的所以結(jié)果
真正的查詢數(shù)據(jù)是這塊代碼$result = $this->connection->find($this);,這段代碼會(huì)執(zhí)行到文件thinkphp/library/think/db/Connection.php
從這塊代碼可以看到當(dāng)查詢一條數(shù)據(jù)時(shí)框架默認(rèn)給加上了limit為1,至于為什么這么加你就需要查看一下sql優(yōu)化方面的知識(shí)了。
在這里就是關(guān)于sql語(yǔ)句的生成,代碼自己好好看看就會(huì)明白,咔咔解析的只是執(zhí)行流程和具體代碼簡(jiǎn)單的了解一下即可。
至于具體實(shí)現(xiàn)流程咔咔在后期如果有機(jī)會(huì)會(huì)單獨(dú)把每個(gè)方法進(jìn)行深度解析,那時(shí)就是主要針對(duì)代碼的解析。
最終返回結(jié)果如下