Eloquent CRUD:find

撈出一筆資料時,Eloquent是如何將資料mapping成物件的呢?

資料庫撈出來的應該是一大堆rows,轉成PHP的陣列還算容易想像,但怎麼轉成Eloquent實體呢?

我們來看看find方法背後做了什麼。

別被new static嚇到了,其實就是new static(),也就是呼叫當前類別的建構式而已。
接著看看newQuery方法。

newBaseQueryBuilder會回傳\Illuminate\Database\Query\Builder實體。
這段重點就是拿\Illuminate\Database\Query\Builder當參數傳入,製作出\Illuminate\Database\Eloquent\Builder實體。
一個專門製造Query,一個專門製作Eloquent,在想像它們的時候是分開的。

接著要呼叫這Eloquent\Builder的find方法。

我不喜歡第一段的防禦性編程。它若發現傳入的是陣列會去呼叫findMany…。這會導致其他Laravel框架開發者濫用find方法、不去分清楚find跟findMany的差別。這是在迴避溝通不良的問題。
參閱:Why Defensive Programming is Rubbish

接著會呼叫Database\Query實體(它在前面的方法中被產生,我省略了這部份)的where方法。
用getQualifiedKeyName產生像是’users.id’的字串給where方法。
其實就是用OOP的方式以Database\Query去寫SQL。

最後,呼叫first方法。

呼叫了take方法…但是source code沒有定義take方法。它到底是什麼?
嘿嘿,別忘了PHP有__call這個magic function。

真正的take出現在倒數第二行。也就是Database\Query的take。
它只是要加入SQL語法的LIMIT而已。

注意最後一行。只有當方法在passthru陣列裡面才會回傳方法結果,不然都是傳回自己。
take, get, first三個方法Database\Query類別都有,導致這段看似一般的Method Chaining,其實完全不是。
take是Query\Builder的、get是Eloquent\Builder的、first是Eloquent\Collection的。
所以這行:

其實等於:


如您所見,這就是magic method會被人詬病的地方。這段code變得超級難trace,不知道到底在呼叫什麼。

這三句,第一句就是用$query設定數量(MySQL的LIMIT),第二句是得到包住多個Eloquent實體的Collection,第三句則是拿出Collection的第一個。

第二句的get就是你常用到的那個get:

來看看它的長相吧。

重點來了。終於能回答一開始的問題
「資料庫撈出來的應該是一大堆rows,轉成PHP的陣列還算容易想像,但怎麼轉成Eloquent實體呢?」
來看第一句的getModels:

解答看起來不遠了!回到Eloquent\Model看這個方法

原來在這裡!終於找到答案了!腦袋都快燒掉了!

複習

好,最後我們來複習一遍。
Eloquent的find依序執行:
一、做出Eloquent\Builder物件,請它根據primary key做出Eloquent\Model實體
二、Eloquent\Builder會拿出Query\Builder實體幫忙,告訴它where敘述
三、Eloquent\Builder接著告訴它limit敘述、拿出rows做成物件、把第一個回傳

以上大致就是一個Eloquent子類別在find時會做的事情。是不是比想像中複雜地多呢:)
我是覺得contributors有點炫技啦,多寫幾行清楚得多。

by 阿川先生