按 long/lat 查询自定义 post 类型以找到最近的

Querying custom post types by long/lat to find nearest

我有一个网站,允许用户输入他们的 post代码,然后他们就可以看到附近的餐馆。我将餐厅存储在 MySQL table 中,每一行包含餐厅名称、地址、营业时间以及经纬度。

当用户输入他们的 post 代码时,我使用 Google API 将 post 代码转换为经度和纬度,然后我使用此 SQL查询...

select 
    locations.*, 
    SQRT(
        69.1*69.1*(latitude - $latitude)*(latitude - $latitude) + 
        53*53*(longitude - $longitude)*(longitude - $longitude)
    ) as distance 
from locations 
having distance <= 100 
order by distance 
limit 100

我正在将站点迁移到 Wordpress。我创建了一个名为 restaurants 的自定义 post 类型,并使用 Advanced Custom Fields 来记录相同的字段 - 名称、地址、营业时间以及经度和纬度。我将经度和纬度存储在不同的字段中。

我可以利用WP_Query查询所有餐厅:

$query = new WP_Query( array( 'post_type' => 'restaurants' ) );

但是我如何修改我的 WP_Query 调用以允许我根据提供的经度和纬度来搜索和排序餐馆?


首先,wordpress 将您的自定义帖子存储在 wp_posts table 中,而 acf 字段存储在 wp_post_meta table 中,因此您必须加入他们以某种方式 sql 。如果我理解你是正确的,你可以使用 wpdb class,它允许你进行自己的 sql 查询。 https://codex.wordpress.org/Class_Reference/wpdb

我认为最好的选择是这个

  1. 在你的主题选项中创建一个文本区域 acf 字段给它提示 <style>.keepitsecret-keeptitsafe{display:none;}</style> 并给那个字段本身一个 class 的 "keepitsecret-keeptitsafe" 所以它对前端用户隐藏

  2. 运行 就像每天的 cronjob(这些餐馆多久更换一次位置,你知道吗?)来获取你 post 的所有 POST_IDs一个经纬度数组,看起来应该是这样的:[1=>['lat'=>12123123, 'lon'=>12123123132], 3=>['lat'=>12123123, 'lon'=>12123123132]] 并将其作为 json 存储在先前制作的 Option ACField 中。 (提示:当你检索时,确保传递 false 以避免格式化你的 json ) get_field('latlon', 'option', false) // 作为一个 bakcup,如果你愿意,请保存到文件


    $allyourcustomposts = get_posts(['post_type'=>['whateveryourcustompostis'], 'posts_per_page'=> -1]);
    foreach($allyourcustomposts as $post) {
        $lat = get_field('lat', $post->ID);
        $lon = get_field('lon', $post->ID);
        if($lat && $lon && $lat != '' && $lon != '') {
            $savior[$post->ID] = ['lat'=>$lat,'lon'=>$lon];
        }
    }
    if(count($savior) > 0) {
        $json = json_encode($savior);
        update_field('field_5b04b0544d603', $json, 'option');
        @file_put_contents('savior.json', $json);
    }
  1. 创建一个函数,使用可选的 $distance = 0 参数查找 2 个位置之间的距离。

  2. 现在,当您搜索时,从存储的 POST_ID=>lat&lon 数组中遍历所有 posts 保存一个单独的 $finals[post_id 数组] = 距离

  3. 对数组进行排序,然后array_keys得到顺序中的所有post id

最后在你的 wp_query 到 $args['post__in'] = $finalspostids,

如果您需要按照 $finalpostid 中的顺序进行排序,$args['orderby'] = 'post__in'

一点起始代码:



    function orderByNearby($lat, $lon, $arrayOfLatLonPosts, $distance = 0) {
        $orderedIds = [];
        if($lat != 0 && $lon != 0 && $arrayOfLatLonPosts && count($arrayOfLatLonPosts) > 0) {
            foreach($arrayOfLatLonPosts as $post_id => $arrayOfLatLonPost) {
                $dist = @closeEnough($arrayOfLatLonPost->lat, $arrayOfLatLonPost->lon, $lat, $lon);
                if($distance > 0) {
                    if($dist  0) {
        asort($ids);
        $args['post__in'] = array_keys($ids);
        $args['orderby'] = 'post__in';
    } else {
        $args['s'] = 'this is hilariously monstreous stupendous name search that will never and probably will ever match!!! so blah blah blah blah blah';
    }

所以当您传递 $distance 变量时,您有时会得到 0 个结果,在这种情况下,只需将极长的搜索文本传递给 return 空。

ps。第一次在这里回答问题,编辑的人渣就是地堡!!!

祝你好运。

VA79 是正确的,这将非常复杂,执行 sql 连接和其他操作。

我找到了一个非常简单且易于实施的解决方案。

1。 首先你需要安装这个插件: https://mg.wordpress.org/plugins/acf-google-maps-radius-search/

2。 在第 40 行的这个(!)插件的 functions.php 中,您必须在变量 $address 中设置高级自定义字段的名称。

3。 在第 163 行的此插件的 acf_gms.class.php 中,您必须在 $address 变量中再次设置您的字段名称。

4。 现在您必须保存 posts,它们具有 Google 地图字段表单高级自定义字段设置,因此具有经度和纬度值。这很重要,否则新创建的数据库 table(感谢插件)将没有其中的值。

5。 保存后,您的数据库中有一个新的 table,其中包含 Post ID、Long 和 Lat Value。感谢插件,太棒了

我现在假设您希望在 post 存档页面上有表单字段,您可以在其中输入地址并按顺序输出 posts,其中最接近的post 首先显示,下一个最接近的 post 依次类推。你想找最近的,所以我想这就是你要找的。

应在您的 archive.php 文件中输入以下代码。请用您的密钥替换 API 密钥。

<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOURAPIKEY&libraries=places"></script>
<script>
    function initialize() {
      var input = document.getElementById('location');
      var options = {
        types: ['(regions)'],
    };
      var autocomplete = new google.maps.places.Autocomplete(input, options);
        google.maps.event.addListener(autocomplete, 'place_changed', function () {
            var place = autocomplete.getPlace();
            document.getElementById('lng').value = place.geometry.location.lng();
            document.getElementById('lat').value = place.geometry.location.lat();
        });
    }
    google.maps.event.addDomListener(window, 'load', initialize);
</script>

如您所见,我们正在从表单字段中获取位置、经度和纬度值。所以表格看起来像这样,可以放在存档 php 文件中。

<form id="yourform">
    <div>
        <input id="location" type="text" name="location" placeholder="Postcode and place of residence" autocomplete="on" runat="server" />  
        <input type="hidden" id="lng" name="lng" />
        <input type="hidden" id="lat" name="lat" />
    </div> <!-- field -->
    <input type="submit" value="Search nearest" />
</form>

如果您在 ID 为 "location" 的输入字段中输入位置,google 地图将提供位置建议。当您提交表单时,我们开始使用的脚本将使用此位置并获取 long 和 lat 值。使用隐藏输入,此值将 posted 到 URL 作为参数 (?lng=...&lat=...)。

使用我们的插件 运行 和新数据库 table,您将获得 post 按您输入的位置的距离排序的数据。

就是这样。如果您有任何问题,请不要介意。