(图片有点大,可右键新tab查看)php
User模型laravel
class User extends Model { public function profile() { return $this->hasMany(UserProfile::class); } }
使用with查询某个user及其的profilesql
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 框架的工做方式相关,咱们先看看下面的例子:ui
咱们使用 DB::listen 方法去记录相关的 sql 语句this
此次咱们不用find,用getspa
User::with('profile')->whereIn('id', [3, 4]) ->get()->toArray()
咱们查看 log 能够发现有如下语句:debug
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 获得的仍是一个空集合。
(对于全部关联都有效哦)