#php #laravel #eloquent
Вопрос:
Возьмите этот рабочий код. Сначала он выполняет поиск в users
таблице по ключевому слову, а затем использует whereHas()
метод поиска в сводной таблице category_user
для любых пользователей, отнесенных к данной категории на основе поиска. Это работает хорошо, все записи извлекаются.
Итак, чтобы быть абсолютно ясным, используются 3 таблицы, users
, categories
и category_user
.
Я использую коллекции Laravel sortByDesc()
для упорядочивания записей на основе релевантности. Итак, если категория 1, категория 2 и ключевое слово из моего примера поискового запроса были сохранены для пользователя 1, и только категория 1 была сохранена для пользователя 2, пользователь 1 будет отображаться первым в результатах.
Приведенный ниже код делает именно это, но ТОЛЬКО для users
таблицы. У меня было хорошее сканирование, и я не могу найти способ применить это решение к этому типу запросов из-за уникальности способа, которым мне нужно заказать. Кто-нибудь может посоветовать какие-либо методы?
//props for sortByDesc
$props = ['name', 'slug', 'location', 'company_name', 'biography', 'usually_active'];
//Search Query
//1. Search users table for keywords
//2. use whereHas() to search pivot table of categories for keyword and filters selected
//3. use sortByDesc collections to order results based number of matches, most matches ordered at the top
$search_results = User::where('approved', '=', 1)
->where('user_type_id', '=', 1)
->orWhere(function ($q) use ($search_items) {
//search users tables
foreach ($search_items as $value) {
$q->orWhere('name', 'like', "%{$value}%");
$q->orWhere('location', 'like', "%{$value}%");
$q->orWhere('company_name', 'like', "%{$value}%");
$q->orWhere('biography', 'like', "%{$value}%");
$q->orWhere('usually_active', 'like', "%{$value}%");
}
})->whereHas('categories', function ($q) use($search_items) {
//search categories table
if(!empty($search_items)):
$q->whereIn('name', $search_items);
$q->orWhereIn('slug', $search_items);
endif;
//Now we sort based on best matches
})->get()->sortByDesc(function($i, $k) use ($search_items, $props) {
// The bigger the weight, the higher the record
$weight = 0;
// Iterate through search terms
foreach($search_items as $item) {
foreach($props as $prop) {
if(strpos($i->{$prop}, $item) !== false)
$weight = 1; // Increase weight if the search term is found
}
}
return $weight;
});
//get the results and paginate
$search_results = $search_results->values()->all();
$search_results = $this->paginate($search_results);
Ответ №1:
Короткий ответ:
Добавьте select для столбцов категорий, которые вы хотите отфильтровать. Будьте осторожны, результирующий набор данных будет содержать столбцы типа categories.name
so, поэтому вы можете соответствующим образом настроить ключи search_items
или псевдоним added select.
Для этого вам нужно присоединиться к таблице categories в запросе, я угадал ваши внешние ключи, но не стесняйтесь исправлять их в предложении join, чтобы они соответствовали тому, что у вас есть в базе данных.
Таким образом, вы можете упростить свои предложения where только в одном
<?
User::join('categories', 'user.categories_id', '=', 'categories.id')
where('approved', '=', 1)
->where('user_type_id', '=', 1)
->orWhere(function ($q) use ($search_items) {
//search users tables
foreach ($search_items as $value) {
$q->orWhere('name', 'like', "%{$value}%");
$q->orWhere('location', 'like', "%{$value}%");
$q->orWhere('company_name', 'like', "%{$value}%");
$q->orWhere('biography', 'like', "%{$value}%");
$q->orWhere('usually_active', 'like', "%{$value}%");
$q->orWhereIn('categories.name', $search_items);
$q->orWhereIn('categories.slug', $search_items);
}
})
// Add the columns you want to sort on
->addSelect(['categories.name', 'categories.slug'])
->get()->sortByDesc(function($i, $k) use ($search_items, $props) {
// The bigger the weight, the higher the record
$weight = 0;
// Iterate through search terms
foreach($search_items as $item) {
foreach($props as $prop) {
if(strpos($i->{$prop}, $item) !== false)
$weight = 1; // Increase weight if the search term is found
}
}
return $weight;
});
Более длинный ответ:
Когда вы запускаете свой запрос из модели, в вашем случае User
, eloquent автоматически добавляет столбцы этой модели в select, независимо от отношений, которые определены в модели.
Если вы хотите получить доступ к столбцам в объединенных таблицах, вам нужно вручную добавить их в select with ->addSelect([...])
.
Вы можете прочитать больше об этом в официальных документах: https://laravel.com/docs/8.x/queries#specifying-a-select-clause
Комментарии:
1. Спасибо, что нашли время ответить. Я чувствую, что это на правильном пути, когда я добавляю в -> addSelect([‘categories.name ‘, ‘categories.slug’]) он пытается вытащить categories.name и категории. извлекается из таблицы users, а не из таблицы categories. Есть ли что-нибудь дополнительное, что требуется для того, чтобы эта оценка работала?
2. О, может быть, вам нужно добавить соединение в таблицу категорий, тогда я отредактирую свой ответ
3. Я добавил объединение и упростил предложение where, поскольку поля категорий теперь доступны в запросе
4. Нужно ли объединять сводную таблицу вместо или вместе с категориями? поскольку таблица categories ничего не хранит для пользователя, сводная таблица category_user хранит.
5. О, есть точка опоры? Итак, у вас много категорий на пользователя? В этом случае, каков именно предполагаемый результат в итоге? Одна строка на пользователя или одна строка на категорию? Возможно, это могло бы быть более понятным для схемы базы данных и предполагаемого примера результата данных
Ответ №2:
Решение состояло в том, чтобы использовать withCount()
метод для определения количества категорий, сохраненных для пользователя в сводной таблице. Затем я мог бы использовать categories_count
returned с каждым элементом для упорядочивания записей на основе количества помеченных категорий на пользователя.
$search_results = User::where('approved', '=', 1)
->where('user_type_id', '=', 1)
->where(function ($q) use ($search_items) {
//search users tables
foreach ($search_items as $value) {
$q->orWhere('users.name', 'like', "%{$value}%");
$q->orWhere('users.location', 'like', "%{$value}%");
$q->orWhere('users.company_name', 'like', "%{$value}%");
$q->orWhere('users.biography', 'like', "%{$value}%");
}
})->orWhereHas('categories', function ($q) use($search_items) {
if(!empty($search_items)):
$q->whereIn('name', $search_items);
$q->orWhereIn('slug', $search_items);
endif;
//Now we sort based on best matches
})->withCount([
'categories' => function ($q) use($search_items) {
//search categories table
if(!empty($search_items)):
$q->whereIn('name', $search_items);
$q->orWhereIn('slug', $search_items);
endif;
}
])->orderByDesc('categories_count')->paginate(25);