Laravel中实现City与Hotel一对多关联模型的联合搜索
解决Laravel中City与Hotel模型的联合搜索问题
嘿,我来帮你搞定这个联合搜索的需求!首先得先纠正一个模型关联的小错误——你的Hotel模型里的city()方法应该用belongsTo而不是hasOne,因为一个酒店属于一个城市,而不是“拥有”一个城市,修正后关联才能正常工作:
1. 修正模型关联
Hotel 模型
public function city() { return $this->belongsTo(City::class, 'city_id'); }
City 模型
你当前的hotel()关联是正确的,保持即可:
public function hotel(){ return $this->hasMany(Hotel::class); }
2. 实现联合搜索API
接下来咱们在控制器里写一个搜索方法,同时查询匹配的城市和酒店,统一返回格式方便前端处理。比如创建一个SearchController:
<?php namespace App\Http\Controllers; use App\Models\City; use App\Models\Hotel; use Illuminate\Http\Request; class SearchController extends Controller { public function search(Request $request) { // 获取用户输入的关键词,默认空字符串避免报错 $keyword = $request->input('keyword', ''); // 1. 查询匹配的城市(按名称模糊匹配) $cities = City::where('name', 'like', "%{$keyword}%") ->select('id', 'name') ->addSelect(['type' => 'city']) // 添加类型标记,方便前端区分城市/酒店 ->get(); // 2. 查询匹配的酒店:既匹配酒店名称,也匹配所属城市的名称 $hotels = Hotel::where('name', 'like', "%{$keyword}%") ->orWhereHas('city', function ($query) use ($keyword) { $query->where('name', 'like', "%{$keyword}%"); }) ->select('id', 'name', 'city_id') ->addSelect(['type' => 'hotel']) ->get(); // 合并两个结果集 $results = $cities->concat($hotels); // 返回JSON响应 return response()->json([ 'success' => true, 'count' => $results->count(), 'data' => $results ]); } }
代码说明:
- 类型标记:通过
addSelect给结果添加type字段,前端可以根据这个字段区分是城市还是酒店,方便展示不同的样式 - 多条件匹配:酒店查询时用
orWhereHas实现“酒店名称匹配”或“所属城市名称匹配”的逻辑,如果你只需要匹配酒店名称,去掉这部分即可 - 安全处理:Laravel的查询构造器会自动处理SQL注入,直接使用
%{$keyword}%是安全的
3. 配置API路由
在routes/api.php中添加搜索路由:
Route::get('/search', [\App\Http\Controllers\SearchController::class, 'search']);
4. 优化建议
如果你的数据量较大,模糊查询的性能会下降,推荐以下优化方案:
- 全文索引:给
cities.name和hotels.name字段添加MySQL全文索引,然后用whereRaw实现更高效的搜索:$cities = City::whereRaw("MATCH(name) AGAINST(? IN BOOLEAN MODE)", [$keyword])->get(); - Laravel Scout:集成Scout+Meilisearch/Algolia,实现更专业的全文搜索、分词搜索、排序等功能
内容的提问来源于stack exchange,提问作者Farshad
相关产品推荐
相关产品推荐

