2つのEloquentコレクションをマージする方法は?


87

質問テーブルとタグテーブルがあります。特定の質問のタグからすべての質問を取得したいと思います。したがって、たとえば、特定の質問に「旅行」、「電車」、「文化」のタグを付けることができます。これら3つのタグのすべての質問を取得できるようにしたいと思います。トリッキーなのは、質問とタグの関係がEloquentでbelongsToManyとして定義されている多対多であるということです。

私は以下のように質問コレクションをマージしようと考えました:

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

しかし、それは機能していないようです。何もマージしていないようです。私はこれを正しく試みていますか?また、Eloquentで多対多の関係にある行の行をフェッチするためのより良い方法はおそらくありますか?


積極的な読み込みとwithメソッドに関するドキュメントを確認しましたか?あなたの問題は、より雄弁なクエリを使用して簡単に解決できます。コンピューターの後ろに来たら、誰かが私を殴らない限り、例を書きます。
ルセオス2015年

1
@Luceoswithは役に立ちません。それwhereHasが必要です-以下の答えのように。
Jarek Tkaczyk 2015年

はい、私の間違い。あなたは正しい
Luceos 2015年

回答:


139

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);
}

1
プッシュを使用してツリーからフラットリストを作成しようとしていましたが、foreachアプローチは本当に役に立ちました。
ジョージ

Eloquentコレクションは通常のコレクションのようには動作しないことに注意してください。それらはgetKey結果をマージするために使用するので、Model::all()->merge(Model::all())->count() === Model::all()->count()
eithed

34

merge()メソッドは、Collection呼び出されたコレクションを変更しません。新しいデータがマージされた新しいコレクションが返されます。必要なものは次のとおりです。

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

しかし、あなたは間違った角度から問題に取り組んでいると思います。

特定の条件を満たす質問を探しているので、その方法でクエリを実行する方がおそらく簡単です。has()そしてwhereHas()方法は、関連レコードの有無に基づいてクエリを生成するために使用されています。

タグのある質問を探しているだけの場合は、このhas()方法を使用します。特定のタグが付いた質問を探しているので、を使用しwhereHas()て条件を追加します。

したがって、「Travel」、「Trains」、または「Culture」のいずれかのタグが少なくとも1つあるすべての質問が必要な場合、クエリは次のようになります。

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

これらの3つのタグすべてを含むすべての質問が必要な場合、クエリは次のようになります。

$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();

1
+ただし、提案した2番目(すべてのタグ)のオプションは簡略化されている可能性があります:stackoverflow.com/a/24706347/784588
Jarek Tkaczyk 2015年

ただし、タグ名をハードコーディングすることはできません。この例では、質問にこれらのタグが含まれていますが、他の質問ではタグが異なります
–AllfaridMoralesGarcía 2018


11

2つの異なる雄弁なコレクションを1つにマージすると、一部のオブジェクトのIDが同じになり、一方が他方を上書きします。代わりにpush()メソッドを使用するか、問題へのアプローチを再考してそれを回避してください。 ウェブを参照


おかげで、これはここにあるコメントに私を置きましたmedium.com/@jeffparr_57441/…それは上書きすることなくきれいに仕事をしているようです。
マーク

1

雄弁なコレクションではすべてが機能しません。laravelの雄弁なコレクションは、マージの問題を引き起こすと思われるアイテムのキーを使用します。最初のコレクションを配列として戻し、それを新しいコレクションに入れてから、他のコレクションをにプッシュする必要があります。新しいコレクション。

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

1

雄弁なコレクションごとに新しいベースコレクションを作成すると、マージが機能します。

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

この場合、主キーによる競合はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.