【问题标题】:Querying custom post types by long/lat to find nearest按 long/lat 查询自定义帖子类型以找到最近的
【发布时间】:2017-12-13 12:53:40
【问题描述】:

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

当用户输入他们的邮政编码时,我使用 Google API 将邮政编码转换为经度和纬度,然后我使用这个 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。我创建了一个名为餐馆的自定义帖子类型,并使用高级自定义字段来记录相同的字段 - 姓名、地址、营业时间以及经度和纬度。我将经度和纬度存储在单独的字段中。

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

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

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


【问题讨论】:

    标签: php wordpress advanced-custom-fields


    【解决方案1】:

    VA79 是对的,这将是相当复杂的,做 sql 连接之类的东西。

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

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

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

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

    4。 现在您必须保存已设置 Google Maps Field 表单 Advanced Custom Fields 并因此具有经度和纬度值的帖子。这很重要,否则新创建的数据库表(感谢插件)将没有值。

    5。 保存后,您的数据库中有一个带有 Post ID、Long 和 Lat 值的新表。感谢插件,太好了。

    我现在假设您希望在帖子存档页面上有表单字段,您可以在其中输入地址并按顺序输出帖子,其中最接近的帖子首先显示,下一个最接近的帖子接下来显示很快。你想找到最近的,所以我想这就是你要找的。​​p>

    以下代码应输入到您的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 为“位置”的输入字段中输入位置,谷歌地图会建议地点。当您提交表单时,我们开始使用的脚本将使用此位置并获取 long 和 lat 值。通过隐藏输入,这些值将作为参数 (?lng=...&lat=...) 发布到 URL。

    随着我们的插件运行和新的数据库表,您将获得按与您输入位置的距离排序的帖子。

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        我认为最好的选择是这个

        1. 在您的主题选项中创建一个文本区域 acf 字段,给它&lt;style&gt;.keepitsecret-keeptitsafe{display:none;}&lt;/style&gt; 的提示,并给该字段本身一个“keepitsecret-keeptitsafe”类,以便它对前端用户隐藏

        2. 像每天的 cronjob 一样运行(这些餐馆多久更换一次位置,你知道吗?)以获取您帖子的所有 POST_ID,其中包含一组 lat&lon,应该如下所示:[1=>[' lat'=>12123123, 'lon'=>12123123132], 3=>['lat'=>12123123, 'lon'=>12123123132]] 并将其作为json存储在先前制作的选项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 数组中的所有帖子,保存一个单独的 $finals[post_id] = distance 数组

        3. 对数组进行排序,然后array_keys按顺序获取所有post id

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

        如果您需要按照 $finalpostids 中的顺序进行排序,则 $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 个结果,在这种情况下只需传递极长的搜索文本以返回空。

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

        祝你好运。

        【讨论】:

        • closeEnough函数就是这个function closeEnough($post_lat, $post_lon, $user_lat, $user_lon, $length = 5) { $theta = $post_lon - $user_lon; $dist = sin(deg2rad($post_lat)) * sin(deg2rad($user_lat)) + cos(deg2rad($post_lat)) * cos(deg2rad($user_lat)) * cos(deg2rad($theta)); $dist = acos($dist); $dist = rad2deg($dist); $miles = $dist * 60 * 1.1515; return $miles; }
        猜你喜欢
        • 1970-01-01
        • 2019-08-28
        • 1970-01-01
        • 2021-06-07
        • 1970-01-01
        • 2022-01-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多