Как объединить две красноречивые коллекции?

86

У меня есть таблица вопросов и таблица тегов. Я хочу получить все вопросы из тегов данного вопроса. Так, например, у меня могут быть теги «Путешествие», «Поезда» и «Культура» к данному вопросу. Я хочу получить все вопросы по этим трем тегам. Сложность, как кажется, в том, что отношения вопросов и тегов - это отношения «многие ко многим», определенные в Eloquent как ownToMany.

Я подумал о попытке объединить Коллекции вопросов, как показано ниже:

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

Хотя, похоже, это не работает. Не похоже ничего объединить. Правильно ли я пытаюсь это сделать? Кроме того, возможно, есть лучший способ получить строку строк в отношении «многие ко многим» в Eloquent?

Мартын
источник
Вы проверяли документацию об активной загрузке и методе with? Ваш вопрос может быть легко решен с помощью более красноречивого запроса. Как только я сяду за компьютер, я напишу пример, если меня не опередят.
Luceos
1
@Luceos withне поможет. Это то, whereHasчто нужно - как в ответе ниже.
Ярек Ткачик
да, моя ошибка; вы правы
Luceos

Ответы:

135

Метод слияния возвращает объединенную коллекцию, он не изменяет исходную коллекцию, поэтому вам необходимо сделать следующее

$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);
}
Wader
источник
1
Я пытался построить плоский список из дерева, использование push было тем, что мне было нужно, но подход foreach действительно помог.
Джордж
Имейте в виду, что коллекции Eloquent не ведут себя как обычные коллекции, т.е. они используют , getKeyчтобы результаты слияния, такModel::all()->merge(Model::all())->count() === Model::all()->count()
eithed
33

merge()Метод на Collectionне изменяет коллекцию , на которой он был назван. Он возвращает новую коллекцию с объединенными новыми данными. Вам потребуется:

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

Однако я думаю, что вы подходите к проблеме не с той точки зрения.

Поскольку вы ищете вопросы, отвечающие определенным критериям, вероятно, будет проще запросить таким образом. has()И whereHas()методы используются для создания запроса на основе существования соответствующей записи.

Если бы вы просто искали вопросы с какими-либо тегами, вы бы использовали этот has()метод. Поскольку вы ищете вопросы с определенным тегом, вы должны использовать whereHas()для добавления условия.

Итак, если вы хотите, чтобы все вопросы содержали хотя бы один тег с «Путешествие», «Поезда» или «Культура», ваш запрос будет выглядеть так:

$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();
Патрик
источник
1
+, однако второй вариант (все теги), который вы предложили, можно упростить: stackoverflow.com/a/24706347/784588
Jarek Tkaczyk
но вы не можете жестко закодировать имена тегов. В этом примере в вопросе есть эти теги, но в других вопросах теги будут
другими
24
$users = User::all();
$associates = Associate::all();

$userAndAssociate = $users->merge($associates);
sh6210
источник
6
Прочитайте эту (перезапись): medium.com/@tadaspaplauskas/...
Jeffz
1
@Jeffz, действительно невероятно, что он объединяет "дубликаты" только на основе идентификатора
Эндрютвебер
11

Объедините две разные красноречивые коллекции в одну, и у некоторых объектов окажется один и тот же идентификатор, один перезапишет другой. Вместо этого используйте метод push () или переосмыслите свой подход к проблеме, чтобы избежать этого. Обратитесь к Интернету

новичок2005
источник
Спасибо, я попал в комментарий, найденный здесь 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;
}
Люк Сноуден
источник
0

Создание новой базовой коллекции для каждой красноречивой коллекции, слияние работает для меня.

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

В этом случае нет конфлитов по его первичным ключам.

Жоао Карлос Жуниор
источник