Laravel 渴望加载数据透视表关系

Posted

技术标签:

【中文标题】Laravel 渴望加载数据透视表关系【英文标题】:Laravel Eager Loading Pivot Table Relations 【发布时间】:2019-12-21 03:11:14 【问题描述】:

在开发我的聊天应用程序时,我已经看了很多帖子很长一段时间了,但我似乎无法掌握如何实现以下目标:

我有什么: 3 表:

    客户

    频道

    channel_client(数据透视表)

2 个型号:

    应用\客户端

    class Client extends Model
    
        protected $table = "clients";
    
    public function channels() 
        return $this->belongsToMany(Channel::class);
     ...
    

    应用\频道

    class Channel extends Model
    public function clients() 
        return $this->belongsToMany(Client::class);
     ...
    

我想要实现的目标: 获取频道,其中包含客户端 X 和客户端 Y,仅此而已。 (我事先可以通过客户端模型访问客户端 ID)

我尝试修改了一下,结果如下:

>>> $client = App\Client::with('channels')->whereIn('id',[1,2])->get()
=> Illuminate\Database\Eloquent\Collection #3303
     all: [
       App\Client #3243
         id: 1,
         name: "John",
         user_id: 1,
         created_at: "2019-08-14 12:55:19",
         updated_at: "2019-08-14 12:55:19",
         channels: Illuminate\Database\Eloquent\Collection #3354
           all: [
             App\Channel #3249
               id: 1,
               name: "Web Development",
               type: null,
               created_at: "2019-08-14 12:52:32",
               updated_at: "2019-08-14 12:52:32",
               pivot: Illuminate\Database\Eloquent\Relations\Pivot #3362
                 client_id: 1,
                 channel_id: 1,
               ,
             ,
             App\Channel #3360
               id: 3,
               name: "ios Development",
               type: null,
               created_at: "2019-08-14 12:52:32",
               updated_at: "2019-08-14 12:52:32",
               pivot: Illuminate\Database\Eloquent\Relations\Pivot #3321
                 client_id: 1,
                 channel_id: 3,
               ,
             ,
             App\Channel #3363
               id: 5,
               name: "android Development",
               type: null,
               created_at: "2019-08-14 12:52:32",
               updated_at: "2019-08-14 12:52:32",
               pivot: Illuminate\Database\Eloquent\Relations\Pivot #3329
                 client_id: 1,
                 channel_id: 5,
               ,
             ,
           ],
         ,
       ,
       App\Client #3283
         id: 2,
         name: "Lucy",
         user_id: 2,
         created_at: null,
         updated_at: null,
         channels: Illuminate\Database\Eloquent\Collection #3286
           all: [
             App\Channel #3364
               id: 1,
               name: "Web Development",
               type: null,
               created_at: "2019-08-14 12:52:32",
               updated_at: "2019-08-14 12:52:32",
               pivot: Illuminate\Database\Eloquent\Relations\Pivot #3248
                 client_id: 2,
                 channel_id: 1,
               ,
             ,
           ],
         ,
       ,
     ],
   
>>> 

正如您在此处看到的,ID 为 1 的频道是用户的相互频道,因为用户 1 和用户 2 都订阅了频道 1。

我知道我的查询返回用户 ID 为 1 或 2 的所有用户及其频道,但如果我转而查询 App\Channel::with('users')... 我无法指定位置子句,因为我不知道它是什么频道 ID 是相互的。这就是我试图通过查询找出的。 所以这里是总结: 我想要一个 Eagerloading Query(否则我会使用 whereHas),它返回我指定的客户的相互通道。 非常感谢您的阅读。 我希望你能帮助我理解这一点。 问候!

编辑:所以从This Laravel Documentation Part 来看,它应该按照@Mohamed Ahmed 的描述工作,但是当我尝试它时,我得到以下结果,它似乎已经加载了所有频道,但也只有我指定的客户端(但仍然查询所有频道,这不是我的真正目标)

    >>> $cha = App\Channel::with(['clients' => function($q) $q->whereIn('id',[1,2]); ])->get();
=> Illuminate\Database\Eloquent\Collection #3359
     all: [
       App\Channel #3365
         id: 1,
         name: "Web Development",
         type: null,
         created_at: "2019-08-14 12:52:32",
         updated_at: "2019-08-14 12:52:32",
         clients: Illuminate\Database\Eloquent\Collection #3403
           all: [
             App\Client #3398
               id: 1,
               name: "John Doe",
               user_id: 1,
               created_at: "2019-08-14 12:55:19",
               updated_at: "2019-08-14 12:55:19",
               pivot: Illuminate\Database\Eloquent\Relations\Pivot #3324
                 channel_id: 1,
                 client_id: 1,
               ,
             ,
             App\Client #3240
               id: 2,
               name: "Lucy",
               user_id: 2,
               created_at: null,
               updated_at: null,
               pivot: Illuminate\Database\Eloquent\Relations\Pivot #3372
                 channel_id: 1,
                 client_id: 2,
               ,
             ,
           ],
         ,
       ,
       App\Channel #3325
         id: 2,
         name: "Android Development",
         type: null,
         created_at: "2019-08-14 12:52:32",
         updated_at: "2019-08-14 12:52:32",
         clients: Illuminate\Database\Eloquent\Collection #3268
           all: [],
         ,
       ,
       ... all the remaining channels here (too much code)

所以它是部分正确的。有什么想法吗?

【问题讨论】:

【参考方案1】:

你绝对可以扭转它。但这意味着需要修补(明白双关语吗?)。

您想要做的是,访问 Channel 模型并执行 whereHas('users')。魔法发生在这里:

$channel = App\Channel::whereHas('clients', function($q)
  $q->whereIn('id', [1,2])->get();
)->get()

您可以在控制器中尝试此操作,或者将其作为返回值。

这基本上做的是,它调用clients 关系并在关系内运行条件。如果您的关系设置正确(与数据透视表设置正确的多对多关系),您应该没有进一步的问题。因此,现在上面的代码正在获取所有具有 id 1 和 2 的客户端的通道。

希望这会有所帮助。

快乐编码:)

【讨论】:

首先,感谢您的评论。 Tinkering 的意思是“php artisan tinker”并在其中编写一些代码,我发布的 sn-p 来自 tinker,因此我不必创建所有内容,只需一时兴起尝试一下。我会尽快试试这个。 是的,我知道修补匠是什么。我想开个玩笑。不过没关系 哦,我很抱歉:D 这是漫长的一天,我有点读过“明白双关语了吗?”呵呵,我已经更新了我的帖子,请你有空的时候看看它,仍然想继续帮助我。 等等,你的意思是所有剩余的频道都没有用户1和2吗? 没错。事情是这样的:只有频道 1 有用户 1 和 2,但是有多个频道,所以在返回的结果中显示频道 1 有用户 1 和 2,(参见 #3365)但频道 2 没有他们(客户端数组是空的)这是正确的。现在的问题是,为什么通道 2(以及所有其他空通道)会返回?

以上是关于Laravel 渴望加载数据透视表关系的主要内容,如果未能解决你的问题,请参考以下文章

渴望加载 Laravel 5 与两个外键的多对多关系

Laravel 5 hasManyThrough 数据透视表

数据透视表的laravel关系问题

Laravel 与数据透视表的关系,关系?

laravel 多对多关系不存储在数据透视表中

Laravel 5.8 在数据透视表上保存一对多关系