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

Laravel Query Builder如何筛选指定经纬度范围的关联Advertisement?

嘿,这个需求我刚好折腾过类似的,给你两种靠谱的实现思路,你可以根据自己的业务场景选:

方法一:使用whereHas关联筛选(更贴合Laravel Eloquent风格)

因为你用的是多对多关联,用whereHas可以很优雅地约束关联的Location模型,在闭包里加入地理范围的判断逻辑就行。这里推荐用MySQL原生的空间函数来计算距离,效率比手写公式高很多:

// 先定义好目标经纬度和搜索半径(这里半径单位是米,比如1000米=1公里)
$targetLong = 116.403874;
$targetLat = 39.914885;
$radius = 1000;

$query = QueryBuilder::for(Advertisement::class)
    ->with('locations')
    ->whereHas('locations', function ($query) use ($targetLong, $targetLat, $radius) {
        // 用MySQL的ST_Distance_Sphere计算两点球面距离(返回单位是米)
        $query->whereRaw(
            "ST_Distance_Sphere(point(long, latitude), point(?, ?)) <= ?",
            [$targetLong, $targetLat, $radius]
        );
    });

// 拿到符合条件的广告数据
$advertisements = $query->get();

兼容旧版MySQL的备选方案

如果你的MySQL版本低于5.7.6(不支持ST_Distance_Sphere),可以用经典的Haversine公式来计算距离:

$query->whereRaw(
    "(6371 * acos(cos(radians(?)) * cos(radians(latitude)) * cos(radians(long) - radians(?)) + sin(radians(?)) * sin(radians(latitude)))) <= ?",
    [$targetLat, $targetLong, $targetLat, $radius/1000]
);

这里6371是地球半径(单位:公里),所以要把原本的米制半径转成公里(比如$radius/1000)。

方法二:直接JOIN关联表(大数据量下性能更优)

如果你的广告和地点数据量很大,whereHas生成的子查询可能会有点慢,这时候直接JOIN中间表和Location表是更好的选择:

$query = QueryBuilder::for(Advertisement::class)
    ->with('locations')
    ->join('advertisement_locations', 'advertisements.id', '=', 'advertisement_locations.advertisement_id')
    ->join('locations', 'advertisement_locations.location_id', '=', 'locations.id')
    ->whereRaw(
        "ST_Distance_Sphere(point(locations.long, locations.latitude), point(?, ?)) <= ?",
        [$targetLong, $targetLat, $radius]
    )
    ->select('advertisements.*') // 只选广告表的字段,避免和关联表字段冲突
    ->distinct(); // 必须加!因为多对多关联会导致同一广告出现多次重复记录

$advertisements = $query->get();

额外优化建议

  • 确保Location表的long和latitude字段是数值类型(比如DECIMAL(10,8)或者FLOAT)
  • 如果经常做地理范围查询,给(long, latitude)建立空间索引,能大幅提升查询速度:
    CREATE SPATIAL INDEX idx_locations_coords ON locations(long, latitude);
    
  • 注意单位统一:ST_Distance_Sphere返回的是米,Haversine公式用6371时返回的是公里,别搞混了!

内容的提问来源于stack exchange,提问作者Jenssen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:04:18