Как построить поисковый запрос на пересечение в pivot-таблице Laravel?
У меня есть 3 таблицы
Книги --> id, название книги
Теги --> id, название тега
книга_тег --> id_книги, id_тега
Книга может иметь много тегов и наоборот тег может иметь много книг (многие-ко-многим) связь. Я хочу организовать поиск таким образом, чтобы выдавались книги только те, что указаны в поиске. Например, в поиске я ищу книги с тегами: комедия и боевик. И поисковый запрос мне выдавал толкьо лишь книги с этими тегами. Чтобы не выдавались книги, которые имеют теги: комедия, боевик и еще какие-то теги.
Я пытался организовать это с помощью WhereIn, однако ничего не получилось, так как как раз в поисковый запрос включаются книги, которые кроме тех поисковых тегов имеют еще дополнительные. Например, я ищу книги с тегами: детектив, комедия, а в поисковый ответ включаются еще книги, которые имеют теги: детектив,комедия,триллер,боевик. То есть, включатся книги, которые кроме поисковых еще имеют дополнительные, что мне не нужно. Вот, что я пытался сделать (однако неудачно, так как дополнительные книги включаются)
foreach ($tags as $tag) {
if ($request->has($tag->tag))
$filteredTags[] = $tag->id;
}
if ($filteredTags != null) {
$booksQuery->whereHas('tags', function ($q) use ($filteredTags) {
$q->whereIn('id',$filteredTags);
});
}
Ответы (2 шт):
Помимо способа в комментарии, можно сделать достаточно интересным способом и пойти от обратного. Т.е.
1. ищем теги НЕ включающие теги для поиска.
$filteredTags= Tags::whereNotIn('id', $tags_id)->get()->pluck('id');
//где $tags_id = [1, 2, 3, ...] те которые пришли в request!
//вывод: [4, 5, 6]
2. Далее просто замените Ваш whereIn в теле функции whereHas на whereNotIn и Вы получите только те книги где есть только интересующие Вас теги
$booksQuery->whereHas('tags', function ($q) use ($filteredTags) {
$q->whereNotIn('id',$filteredTags);
});
К сожалению с laravel не работал. Вариантов как мне видится 2.
Создать запрос, который сначала находит все книги, у которых всего 2 тега, а потом уже находит те, у которых есть нужные теги. Пример привел ниже. Если по простому: третий
INNER JOINнаходит книги, у которых 2 тега, а дальше уже основной запрос, который фильтрует по нужным тегамSELECT DISTINCT book.idbook, book.name FROM book INNER JOIN book_tag ON book.idbook = book_tag.id_book INNER JOIN tag ON book_tag.id_tag = tag.idtag AND (tag.name = "t1" OR tag.name = "t2") INNER JOIN (SELECT book.idbook, book.name FROM book INNER JOIN book_tag ON book.idbook = book_tag.id_book GROUP BY book.idbook HAVING count(book_tag.id_book) = 2) as Tbl ON Tbl.idbook = book.idbook;Делать выборку по нужным тегам простым запросом, и потом уже с помощью PHP удалять книги, у коготорых есть ненужные теги.