如何在 parent 和 child 上应用 Laravel Eloquent where() 条件(hasMany() 关系)

How to apply a Laravel Eloquent where() condition on both parent and child (hasMany() relationship)

我有一个名为 Laravel Eloquent 的模型 EmailList 和一个名为 Subscriber 的模型。 我在 EmailList 上定义了以下 hasMany 关系。

    public function subscribers() {
        return $this->hasMany(Subscriber::class);
    }

这种关系很好。

我现在想在 parent 上应用 where() 条件,在 child 上应用 where() 条件。

例如,我有两个 EmailList,一个 属性 id 和一个 属性 称为 listname(“列表 A”和“清单 B”)。我的 Subscriber 模型有四个相关属性:idnameemailemail_list_id.

问题:我想从某个列表中获取所有订阅者,例如'List A',并使用 $query(使用 Livewire 的搜索功能).

过滤订阅者

为了实现这一点,我想出了以下代码:

EmailList::where('listname', $listname)->first()
                                    ->subscribers()
                                    ->where('name', 'LIKE', '%'.$this->search.'%')
                                    ->orWhere('email', 'LIKE', '%'.$this->search.'%')
                                    ->paginate(50) //or get(), if you like

此代码部分起作用,因为确实过滤了订阅者,但没有过滤 EmailList。换句话说,我只是搜索了所有订阅者,而不是特定列表中的订阅者。

经过大量搜索后,我发现我或许应该使用 whereHas(),但是当我尝试这样做时,它只给了我电子邮件列表而不是订阅者。

如果有人能帮我过滤一下就好了。对于有经验的开发人员来说,这可能是花生,但它对我有很大帮助!

更新: 看到 EricLandheer 的回答后,我意识到(当然)我也有 EmailList id。也许这会使它更简单一些,这样您就可以使用 find() 而不是 where()->first() 子句。 (我使用 first() 是因为‘listname’是唯一的,我只想要一个实例。对吗?)

目前,您的代码只有 returns 第一个 EmailList。你是故意的,还是只想要第一个 EmailList$listname?

这是一个使用 Eager loading with constraints 的解决方案,它用回调覆盖了 subscribers 关系。结果应该是所有 EmailLists$listname 和匹配的订阅者与搜索。

EmailList::where('listname', $listname)
    ->with('subscribers', function($query) {
        return $query->where('name', 'LIKE', '%'.$this->search.'%')
            ->orWhere('email', 'LIKE', '%'.$this->search.'%')
    })->get();

关于问题更新

确实,用Listname就可以了。如果它是唯一的,那没问题。您可以将 find() 与 ID 一起使用,如果您考虑在稍后阶段更改 EmailList 的名称,这是更可取的。

也许更优雅/可读的解决方案是:

$emailList = EmailList::firstWhere('listname', $listname);

$emailList->subscribers = $emailList->subscribers()
    ->where('name', 'LIKE', '%'.$this->search.'%')
    ->orWhere('email', 'LIKE', '%'.$this->search.'%')
    ->get();

如果您在更多地方使用它,您可以将搜索重构为模型/服务:

// EmailList model

public function subscribers() 
{
    return $this->hasMany(Subscriber::class);
}

public function searchSubscribers(string $search): Collection 
{
    return $this->subscribers()
        ->where('name', 'LIKE', '%'.$this->search.'%')
        ->orWhere('email', 'LIKE', '%'.$this->search.'%')
        ->get();
}

最终代码

$emailList = EmailList::firstWhere('listname', $listname);

$emailList->subscribers = $emailList->searchSubscribers($this->search);