laravel5.1 eloquent with 通過閉包篩選特定 field 得不到結果的問題
(圖片有點大,可右鍵新tab查看)
User模型
class User extends Model { public function profile() { return $this->hasMany(UserProfile::class); } }
使用with查詢某個user及其的profile
User::with([‘profile‘ => function($query) { $query->select([‘id‘]); }])->find(4)->toArray()
上面的用法中,我們會發現,即使數據庫有記錄,sql也記錄了對應的查詢語句,但是profile關聯卻是空的,
但是加上外鍵就可以得到正確結果了:
User::with([‘profile‘ => function($query) { $query->select([‘id‘, ‘user_id‘]); }])->find(4)->toArray()
可以查找到正確的profile了。
這和 laravel 框架的工作方式相關,我們先看看下面的例子:
我們使用 DB::listen 方法去記錄相關的 sql 語句
這次我們不用find,用get
User::with(‘profile‘)->whereIn(‘id‘, [3, 4]) ->get()->toArray()
我們查看 log 可以發現有以下語句:
select * from `tb_user` where `id` in (?, ?) [3,4] select * from `tb_user_profile` where `tb_user_profile`.`user_id` in (?, ?) [3,4]
我們可以明顯發現,laravel 對於 user 和 user_profile 是獨立查詢的,
也就是說會得到兩個集合,一個是 User、一個是 UserProfile,
但是這並不是我們想要的結果,我們需要的結果是,只有一個 User 集合, 並且這個 User 集合裏面有 UserProfile 關聯。
但是結果就是這樣,如果是你,你會怎麽把這些數據關聯起來呢?
對了,我們定義關聯的時候不是定義了它們的關聯方式麽?
上面的 hasMany 方法默認第二第三個參數其實就是這兩個集合建立關聯的關鍵,第三個參數 user_id、第四個參數 id;
這樣一來我們就可以通過比較 UserProfile 的 user_id 和 User 裏面的 id,如果相等,則這個 UserProfile 是屬於這個 User 的,我們就把該 UserProfile 放進 User 的 profile 關聯中,最後就得到我們想要的結果了。
用xdebug證實一下我們的想法:
如我們所想的那樣,圖一的 match 方法,顧名思義就是匹配了,通過 user 模型集合和 profile 模型集合進行匹配。
圖二,也證實了我們模型建立關聯需要通過關聯中外鍵的值得想法。
圖三,是通過獲取 user 的 localkey,也就是 id 的值,來查找 $dictonary 中是否有對應的值,buildDictonary 方法會建立一個關聯數組,key 是 user_id(外鍵)的值,值是關聯的數據。這樣一樣,由於我們沒有把 user_id 也select 出來,最後得到的 $dictonary 的結構並不是預期的那樣:
其實我們本來是想要得到下面的這種:
[ 3 => xxx(UserProfile對象) // 3 是關聯的 user_id ]
但是我們得到的卻是,所有的 UserProfile 都在一個嵌套的數組裏面了,這樣一來,下面的 getRelationValue 得到的結果自然就是空的了。
好了,總結一下,就是:laravel 先查詢主要的數據(不帶with),查詢完了之後,取出其中的 id 列數組(不一定都是id啊,只是舉個例子),將這個數組作為條件去查找關聯,有多少個關聯就會再去查找多少次,查找完關聯之後通過得到的結果的主鍵和關聯數據的外鍵比對,相等則建立關聯。
總結:在關聯篩選 field 的時候,也必須要把關聯的外鍵寫進去,否則,即使產生了正確的 sql 語句,但是它們建立不了關聯,通過 $user->profile 得到的還是一個空集合。
(對於所有關聯都有效哦)
laravel5.1 eloquent with 通過閉包篩選特定 field 得不到結果的問題