symfony 2 不提供地图服务集成,poi搜索必须调用百度地图web api;foselasticabundle不适用,正确做法是用guzzle封装http请求,配置ak/sk、生成sn签名,解析location等关键字段。

Symfony 2 本身不提供地图服务集成能力,POI搜索必须走百度地图 Web API,不能靠 Doctrine 或原生数据库查询实现。
用 FOSElasticaBundle?别试了,它不处理 POI
FOSElasticaBundle 是为全文检索设计的,和地理坐标、POI 检索完全无关。有人误以为“Elasticsearch 支持 geo_point 就能查周边 POI”,但百度地图的 POI 数据不在你的 ES 里——你得先从 https://api.map.baidu.com/place/v2/search 拿到数据,再决定要不要存进 ES。直接拿 FOSElasticaBundle 去调百度接口,代码会报 Class not found 或 Invalid service configuration。
正确做法:用 Guzzle 发起 HTTP 请求 + 自定义 Service
你需要一个轻量、可控、可复用的服务类来封装百度 POI 搜索逻辑。重点不是“集成框架”,而是“安全调用外部 API”。
-
ak必须配置在parameters.yml中,绝不能硬编码;若用 Symfony 2.8+,建议加环境变量前缀防止泄露 - 每次请求必须带
sn签名(MD5(参数字符串 + sk)),否则返回INVALID_SIGN -
scope=2才能拿到经纬度(location.lat/location.lng),scope=1只返回基础字段,很多字段为空 - 分页靠
page_num和page_size,但百度最多返回 400 条(20 页 × 20 条),超量需换关键词或区域
示例 Service 方法片段:
public function searchPoi($keyword, $region, $pageNum = 0)
{
$ak = $this->container->getParameter('baidu_map_ak');
$sk = $this->container->getParameter('baidu_map_sk');
$params = [
'query' => $keyword,
'region' => $region,
'output' => 'json',
'ak' => $ak,
'page_size' => 20,
'page_num' => $pageNum,
'scope' => 2,
];
// sn 签名生成(注意:参数必须按字典序拼接,且 sk 放最后)
ksort($params);
$queryStr = http_build_query($params) . '&sk=' . $sk;
$params['sn'] = md5(urlencode($queryStr));
$client = new \GuzzleHttp\Client();
$res = $client->get('https://api.map.baidu.com/place/v2/search', [
'query' => $params,
]);
return json_decode($res->getBody(), true);
}
结果解析时最容易漏掉的三个字段
百度返回的 results 数组里,真正可用的 POI 字段不是全都有值:
-
uid是唯一标识,用于后续调/place/v2/detail获取营业时间、电话等扩展信息;但免费版 API 默认不返回,需确认是否开通“详情服务”权限 -
location是对象(不是字符串),结构为{"lat": 39.9042, "lng": 116.4074},直接json_decode后可取$poi['location']['lat'] -
detail_url字段在scope=2下才出现,但链接含临时 token,5 分钟后失效,不能长期存储或前端跳转
别指望一次请求拿全数据——百度对高频、大范围、低价值关键词(如“餐厅”“商店”)会限流或降权,真实项目中要结合 tag 参数(如 美食,快餐)缩小语义范围,并缓存高频 region+keyword 组合的结果。











