如何使用Laravel API实现附近地点展示功能
如何在Laravel 5.6中实现基于经纬度查找附近地点的REST API功能?
嘿,作为Laravel新手能写到这一步已经很棒啦!先给你点个赞~你的术语用得没问题,REST API的思路完全在线,咱们一步步来完善这个附近地点查询的功能。
首先,先确认你的parties表已经有存储经纬度的字段,比如lat(纬度)和lng(经度),字段类型建议用DECIMAL(10,8)和DECIMAL(11,8),这样精度足够日常使用。
接下来给你两种实现方案,你可以根据自己的数据库环境选择:
方案一:使用Haversine公式(兼容性强,适合所有数据库)
Haversine公式是计算球面两点距离的经典算法,不需要数据库支持空间扩展,非常适合新手快速实现。你可以把原来的代码补全成这样:
public function index(\Illuminate\Http\Request $request) { // 按活动分类查询 if ($request->has('party_category')) { $parties = Parties::where('party_category', $request->party_category)->get(); } // 按经纬度查找附近地点 elseif ($request->has('lat') && $request->has('lng')) { // 获取传入的经纬度 $lat = $request->input('lat'); $lng = $request->input('lng'); // 设置搜索半径(单位:公里,这里设为10公里,你可以根据需求调整) $radius = 10; // 使用Haversine公式计算距离并筛选 $parties = Parties::selectRaw("*, (6371 * acos(cos(radians(?)) * cos(radians(lat)) * cos(radians(lng) - radians(?)) + sin(radians(?)) * sin(radians(lat)))) AS distance", [$lat, $lng, $lat]) ->having('distance', '<=', $radius) ->orderBy('distance') ->get(); } // 默认返回所有活动 else { $parties = Parties::all(); } return response()->json($parties); }
代码说明:
6371是地球的平均半径(公里),如果要换成英里可以用3956selectRaw里的SQL会计算每个活动到指定经纬度的距离,并给这个距离起别名distancehaving用来筛选出距离在设定半径内的结果orderBy('distance')可以让结果按从近到远排序
方案二:使用Laravel空间查询(更优雅,需数据库支持)
如果你的数据库是MySQL 5.7+或者PostgreSQL,建议把经纬度字段改成POINT类型(空间数据类型),这样可以用Laravel的空间查询功能,代码更简洁:
- 先修改数据库表,添加空间字段(或者修改原有字段):
ALTER TABLE parties ADD location POINT NOT NULL; -- 或者替换原有lat和lng字段: -- ALTER TABLE parties DROP COLUMN lat, DROP COLUMN lng; -- ALTER TABLE parties ADD location POINT NOT NULL;
(注意:如果是新建表,迁移文件里可以用$table->point('location');)
- 然后在
Parties模型里添加空间字段的配置:
protected $casts = [ 'location' => 'point', ];
- 最后修改控制器的查询代码:
public function index(\Illuminate\Http\Request $request) { if ($request->has('party_category')) { $parties = Parties::where('party_category', $request->party_category)->get(); } elseif ($request->has('lat') && $request->has('lng')) { $lat = $request->input('lat'); $lng = $request->input('lng'); $radius = 10; // 公里 $parties = Parties::whereRaw('ST_Distance_Sphere(location, POINT(?, ?)) <= ? * 1000', [$lng, $lat, $radius]) ->orderByRaw('ST_Distance_Sphere(location, POINT(?, ?))', [$lng, $lat]) ->get(); } else { $parties = Parties::all(); } return response()->json($parties); }
代码说明:
ST_Distance_Sphere是MySQL的空间函数,用来计算两点之间的球面距离(单位:米),所以要把公里转换成米(*1000)- 注意:
POINT的参数顺序是经度在前,纬度在后,别搞反了!
额外建议:
- 记得给经纬度字段添加索引,比如方案一给
lat和lng加普通索引,方案二给location加空间索引,这样查询速度会快很多 - 可以添加参数验证,比如用Laravel的
$request->validate()来确保lat和lng是有效的数字,避免报错:
// 在方法开头添加 $request->validate([ 'lat' => 'required_with:lng|numeric|between:-90,90', 'lng' => 'required_with:lat|numeric|between:-180,180', 'party_category' => 'nullable|exists:parties,party_category', ]);
这样你的API就能同时支持按分类查询和按经纬度查附近地点啦,有问题随时调整~
内容的提问来源于stack exchange,提问作者Baran KARABOGA
相关产品推荐
相关产品推荐

