Jak połączyć dwie wymowne kolekcje?

86

Mam tabelę pytań i tabelę tagów. Chcę pobrać wszystkie pytania z tagów danego pytania. Na przykład mogę mieć przypisane do danego pytania tagi „Podróż”, „Pociągi” i „Kultura”. Chcę mieć możliwość pobrania wszystkich pytań dotyczących tych trzech tagów. Problem, jak się wydaje, polega na tym, że relacja między pytaniami i tagami jest zdefiniowana w elokwentnym elokwencji jako „naleŜycie do wielu”.

Myślałem o próbie scalenia kolekcji pytań, jak poniżej:

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

Wydaje się jednak, że to nie działa. Wydaje się, że nic nie łączy. Czy robię to poprawnie? Czy jest też może lepszy sposób na pobranie wiersza wierszy w relacji wiele do wielu w elokwentnym?

Martyn
źródło
Czy sprawdziłeś dokumentację dotyczącą chętnego ładowania i metody? Twój problem można łatwo rozwiązać za pomocą bardziej elokwentnego zapytania. Kiedy już siadam za komputerem, napiszę przykład, chyba że ktoś mnie pobije.
Luceos
1
@Luceos withnie pomoże. To whereHasjest potrzebne - jak w odpowiedzi poniżej.
Jarek Tkaczyk
tak, mój błąd; masz rację
Luceos

Odpowiedzi:

135

Metoda merge zwraca scaloną kolekcję, nie powoduje mutacji oryginalnej kolekcji, dlatego należy wykonać następujące czynności

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

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

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

Zastosowanie przykładu do kodu

$related = new Collection();

foreach ($question->tags as $tag)
{
    $related = $related->merge($tag->questions);
}
Ptak brodzący
źródło
1
Próbowałem zbudować płaską listę z drzewa, używając push było tym, czego potrzebowałem, ale podejście foreach naprawdę pomogło.
George,
Pamiętaj, że kolekcje Eloquent nie zachowują się jak zwykłe Kolekcje, tj. używają getKeydo łączenia wyników, więcModel::all()->merge(Model::all())->count() === Model::all()->count()
eithed
33

merge()Metoda na Collectionnie zmodyfikować kolekcję, w którym została wywołana. Zwraca nowy zbiór ze scalonymi nowymi danymi. Potrzebujesz:

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

Myślę jednak, że rozwiązujesz problem z niewłaściwej perspektywy.

Ponieważ szukasz pytań spełniających określone kryteria, prawdopodobnie łatwiej byłoby zadawać pytania w ten sposób. has()I whereHas()metody są wykorzystywane do generowania kwerendę opartą na istnieniu powiązanego rekordu.

Gdybyś szukał tylko pytań, które mają jakikolwiek tag, użyłbyś tej has()metody. Ponieważ szukasz pytań z określonym tagiem, możesz użyć, whereHas()aby dodać warunek.

Tak więc, jeśli chcesz wszystkie pytania, które mają co najmniej jeden tag z „Podróż”, „Pociągi” lub „Kultura”, Twoje zapytanie będzie wyglądać następująco:

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

Jeśli chcesz, aby wszystkie pytania zawierały wszystkie te trzy tagi, zapytanie wyglądałoby następująco:

$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
źródło
1
+, jednak sugerowana przez Ciebie druga opcja (wszystkie tagi) może być uproszczona: stackoverflow.com/a/24706347/784588
Jarek Tkaczyk
ale nie możesz zakodować na stałe nazw znaczników. W tym przykładzie tak się składa, że ​​pytanie ma te tagi, ale w innych pytaniach tagi będą się różnić
Allfarid Morales García
24
$users = User::all();
$associates = Associate::all();

$userAndAssociate = $users->merge($associates);
sh6210
źródło
6
Przeczytaj (nadpisując): medium.com/@tadaspaplauskas/…
Jeffz
1
@Jeffz to naprawdę niewiarygodne, że łączy „duplikaty” na podstawie samego identyfikatora
andrewtweber
11

Połącz dwie różne wymowne kolekcje w jedną, a niektóre obiekty będą miały ten sam identyfikator, jeden nadpisze inny. Zamiast tego użyj metody push () lub przemyśl swoje podejście do problemu, aby tego uniknąć. Zapoznaj się z siecią

newbie2005
źródło
Dzięki, to skierowało mnie do komentarza znalezionego tutaj medium.com/@jeffparr_57441/ ... który wydaje się czysto wykonywać pracę bez nadpisywania.
Mark
1

Wszystkie nie działają u mnie na elokwentnych kolekcjach , laravel elokwentne kolekcje używają klucza z pozycji , które myślę, że powoduje problemy z łączeniem, musisz odzyskać pierwszą kolekcję jako tablicę, umieścić ją w nowej kolekcji, a następnie wrzucić pozostałe nowa kolekcja;

public function getFixturesAttribute()
{
    $fixtures = collect( $this->homeFixtures->all() );
    $this->awayFixtures->each( function( $fixture ) use ( $fixtures ) {
        $fixtures->push( $fixture );
    });
    return $fixtures;
}
Luke Snowden
źródło
0

Tworząc nową kolekcję podstawową dla każdej elokwentnej kolekcji, scalanie działa dla mnie.

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

W tym przypadku nie miej conflits według kluczy podstawowych.

João Carlos Junior
źródło