laravel5.1 eloquent with 通过闭包筛选特定 field 得不到结果的问题

Posted 白桂任的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 得不到结果的问题的主要内容,如果未能解决你的问题,请参考以下文章

方法 Illuminate\Database\Eloquent\Collection::with 不存在 [关闭]

Laravel Eloquent 和 Query Builder “with (nolock)”

Laravel Eloquent with()-> 返回 null

在 Laravel Eloquent 中使用 with() 返回对象

在 Laravel Eloquent 中使用“with()”函数连接列

有啥方法可以避免在 Laravel 5.7 Eloquent 中加载父级时加载这些 $with 模型?