Wie man zwei beredte Sammlungen zusammenführt?

85

Ich habe eine Fragentabelle und eine Tags-Tabelle. Ich möchte alle Fragen von Tags einer bestimmten Frage abrufen. So kann ich beispielsweise die Tags "Reisen", "Züge" und "Kultur" an eine bestimmte Frage anhängen. Ich möchte in der Lage sein, alle Fragen für diese drei Tags abzurufen. Das Knifflige, so scheint es, ist, dass die Beziehung zwischen Fragen und Tags ein Viele-zu-Viele-Verhältnis ist, das in Eloquent als zu ToMany gehörend definiert wird.

Ich habe darüber nachgedacht, die Fragen Sammlungen wie folgt zusammenzuführen:

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

Es scheint jedoch nicht zu funktionieren. Scheint nichts zu verschmelzen. Versuche ich das richtig? Gibt es in Eloquent vielleicht auch eine bessere Möglichkeit, eine Reihe von Zeilen in einer Viele-zu-Viele-Beziehung abzurufen?

Martyn
quelle
Haben Sie die Dokumentation zum eifrigen Laden und zur with-Methode überprüft? Ihr Problem könnte leicht mit einer besser beredten Abfrage gelöst werden. Sobald ich hinter einem Computer stehe, schreibe ich ein Beispiel, es sei denn, jemand hat mich geschlagen.
Luceos
1
@Luceos withwird nicht helfen. Es ist das whereHas, was benötigt wird - wie in der Antwort unten.
Jarek Tkaczyk
ja, mein Fehler; Sie sind richtig
Luceos

Antworten:

133

Die Merge-Methode gibt die zusammengeführte Sammlung zurück. Sie mutiert nicht die ursprüngliche Sammlung. Daher müssen Sie die folgenden Schritte ausführen

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

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

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

Anwenden des Beispiels auf Ihren Code

$related = new Collection();

foreach ($question->tags as $tag)
{
    $related = $related->merge($tag->questions);
}
Wader
quelle
1
Ich habe versucht, eine flache Liste aus einem Baum zu erstellen. Push war das, was ich brauchte, aber der foreach-Ansatz hat wirklich geholfen.
George
Denken Sie daran, dass sich eloquente Sammlungen nicht wie normale Sammlungen verhalten, d. H. sie verwenden , getKeyum merge Ergebnisse, soModel::all()->merge(Model::all())->count() === Model::all()->count()
eithed
32

Die merge()Methode auf dem Collectionändert nicht die Sammlung, für die sie aufgerufen wurde. Es wird eine neue Sammlung mit den neu zusammengeführten Daten zurückgegeben. Sie benötigen:

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

Ich denke jedoch, dass Sie das Problem aus dem falschen Blickwinkel angehen.

Da Sie nach Fragen suchen, die bestimmte Kriterien erfüllen, ist es wahrscheinlich einfacher, diese auf diese Weise abzufragen. Die Methoden has()und whereHas()werden verwendet, um eine Abfrage basierend auf dem Vorhandensein eines zugehörigen Datensatzes zu generieren.

Wenn Sie nur nach Fragen suchen, die ein Tag haben, würden Sie die has()Methode verwenden. Da Sie nach Fragen mit einem bestimmten Tag suchen, würden Sie das verwenden whereHas(), um die Bedingung hinzuzufügen.

Wenn Sie also alle Fragen haben möchten, die mindestens ein Tag mit "Reisen", "Züge" oder "Kultur" haben, sieht Ihre Anfrage folgendermaßen aus:

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

Wenn Sie alle Fragen mit allen drei Tags haben möchten, sieht Ihre Anfrage folgendermaßen aus:

$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();
patricus
quelle
1
+, aber die 2. Option (alle Tags), die Sie vorgeschlagen haben, könnte vereinfacht werden: stackoverflow.com/a/24706347/784588
Jarek Tkaczyk
Sie können die Tag-Namen jedoch nicht fest codieren. In diesem Beispiel hat die Frage zufällig diese Tags, aber in anderen Fragen variieren die Tags
Allfarid Morales García
24
$users = User::all();
$associates = Associate::all();

$userAndAssociate = $users->merge($associates);
sh6210
quelle
6
Lesen Sie dies (überschreiben): medium.com/@tadaspaplauskas/…
Jeffz
1
@ Jeffz es ist wirklich unglaublich, dass es "Duplikate" nur basierend auf der ID zusammenführt
andrewtweber
11

Füge zwei verschiedene eloquente Sammlungen zu einer zusammen und einige Objekte haben zufällig dieselbe ID, eines überschreibt das andere. Verwenden Sie stattdessen die push () -Methode oder überdenken Sie Ihre Herangehensweise an das Problem, um dies zu vermeiden. Siehe Web

newbie2005
quelle
Vielen Dank, dies brachte mich zu dem Kommentar, der hier zu finden ist. Medium.com/@jeffparr_57441/…, der die Arbeit sauber zu machen scheint, ohne sie zu überschreiben.
Mark
1

Bei eloquenten Sammlungen funktionieren nicht alle für mich. Bei eloquenten Sammlungen von Laravel wird der Schlüssel aus den Elementen verwendet, die meiner Meinung nach zu Zusammenführungsproblemen führen. Sie müssen die erste Sammlung als Array zurückerhalten, diese in eine neue Sammlung einfügen und dann die anderen in die Sammlung verschieben die neue Kollektion;

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

Beim Erstellen einer neuen Basissammlung für jede beredte Sammlung funktioniert die Zusammenführung für mich.

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

In diesem Fall haben keine Konflikte mit den Primärschlüsseln.

João Carlos Junior
quelle