【问题标题】:How to Merge Two Eloquent Collections?如何合并两个 Eloquent 集合?
【发布时间】:2015-08-11 21:41:41
【问题描述】:

我有一个问题表和一个标签表。我想从给定问题的标签中获取所有问题。因此,例如,我可能将标签“旅行”、“火车”和“文化”附加到给定问题上。我希望能够获取这三个标签的所有问题。看起来棘手的是问题和标签关系是在 Eloquent 中定义为 belongsToMany 的多对多关系。

我想过尝试合并问题集合如下:

foreach ($question->tags as $tag) {
    if (!isset($related)) {
        $related = $tag->questions;
    } else {
        $related->merge($tag->questions);
    }
}

但它似乎不起作用。似乎没有合并任何东西。我是否正确地尝试了这个?另外,在 Eloquent 中是否有更好的方法来获取多对多关系中的一行行?

【问题讨论】:

  • 您是否查看过有关预加载和 with 方法的文档?使用更好的 eloquent 查询可以轻松解决您的问题。一旦我掌握了计算机,我就会写一个例子,除非有人打败了我。
  • @Luceos with 无济于事。需要whereHas - 就像下面的答案一样。
  • 是的,我的错;你是对的

标签: php laravel eloquent laravel-collection


【解决方案1】:

Collection 上的 merge() 方法不会修改调用它的集合。它返回一个包含新数据的新集合。您需要:

$related = $related->merge($tag->questions);

但是,我认为您从错误的角度解决问题。

由于您要查找符合特定条件的问题,因此以这种方式查询可能会更容易。 has() 和 whereHas() 方法用于根据相关记录的存在生成查询。

如果您只是在寻找带有任何标签的问题,您可以使用has() 方法。由于您要查找带有特定标签的问题,因此您可以使用 whereHas() 添加条件。

因此,如果您希望所有问题至少有一个带有“Travel”、“Trains”或“Culture”的标签,您的查询将如下所示:

$questions = Question::whereHas('tags', function($q) {
    $q->whereIn('name', ['Travel', 'Trains', 'Culture']);
})->get();

如果您想要包含所有这三个标签的所有问题,您的查询将如下所示:

$questions = Question::whereHas('tags', function($q) {
    $q->where('name', 'Travel');
})->whereHas('tags', function($q) {
    $q->where('name', 'Trains');
})->whereHas('tags', function($q) {
    $q->where('name', 'Culture');
})->get();

【讨论】:

  • +,但是您建议的第二个(所有标签)选项可能会被简化:stackoverflow.com/a/24706347/784588
  • 但您不能对标签名称进行硬编码。在这个例子中,问题恰好有这些标签,但在其他问题中,标签会有所不同
【解决方案2】:

merge 方法返回合并后的集合,它不会改变原始集合,因此您需要执行以下操作

$original = new Collection(['foo']);

$latest = new Collection(['bar']);

$merged = $original->merge($latest); // Contains foo and bar.

将示例应用到您的代码中

$related = new Collection();

foreach ($question->tags as $tag)
{
    $related = $related->merge($tag->questions);
}

【讨论】:

  • 我试图从树中构建一个平面列表,使用 push 是我所需要的,但 foreach 方法确实有帮助。
  • 请记住,Eloquent 集合的行为与普通集合不同,即。他们使用getKey 来合并结果,所以Model::all()->merge(Model::all())->count() === Model::all()->count()
  • 请注意,如果提供的 $items 中存在具有相同值的字符串键,则合并方法将替换原始第二个集合的项目中的任何第一个集合。如果第一个集合键是数字,则第二个集合将添加到新集合的末尾laravel.com/docs/8.x/collections#method-merge
【解决方案3】:

将两个不同的 eloquent 集合合并为一个,一些对象碰巧有相同的 id,一个会覆盖另一个。改用 push() 方法或重新考虑解决问题的方法以避免这种情况。 Refer to web

【讨论】:

  • 谢谢,这让我看到了medium.com/@jeffparr_57441/… 此处的评论,这似乎干净利落地完成了这项工作而不会被覆盖。
  • 使用 ->push() 更安全
【解决方案4】:
$users = User::all();
$associates = Associate::all();

$userAndAssociate = $users->merge($associates);

【讨论】:

【解决方案5】:

eloquent collections 都不适合我,laravel eloquent collections 使用 我认为 导致合并问题的项目中的密钥,您需要获取第一个集合作为一个数组返回,将其放入一个新的集合中,然后将其他的推入新的集合中;

public function getFixturesAttribute()
{
    $fixtures = collect( $this->homeFixtures->all() );
    $this->awayFixtures->each( function( $fixture ) use ( $fixtures ) {
        $fixtures->push( $fixture );
    });
    return $fixtures;
}

【讨论】:

    【解决方案6】:

    为每个 eloquent 集合创建一个新的基础集合,合并对我有用。

    $foo = collect(Foo::all());
    $bar = collect(Bar::all());
    $merged = $foo->merge($bar);
    

    在这种情况下,它的主键没有冲突。

    【讨论】:

      【解决方案7】:

      我想补充一点,我发现 concat 方法似乎没有基于 ID 覆盖,而 merge 方法可以。 concat 似乎对我有用,而 merge 引起了问题。

      【讨论】:

        【解决方案8】:

        我在使用合并时遇到了一些问题。所以我使用了concat。你可以像下面这样使用它。

        $users = User::all();
        $associates = Associate::all();
        $userAndAssociate = $users->concat($associates);
        

        【讨论】:

          猜你喜欢
          • 2014-05-29
          • 2023-03-07
          • 2019-09-08
          • 1970-01-01
          • 2020-07-22
          • 1970-01-01
          • 2017-04-12
          • 2011-11-24
          • 2019-10-13
          相关资源
          最近更新 更多