You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:03:24