-
-
Save akshuvo/4c37df4bd128eb801b7739748ee3cd65 to your computer and use it in GitHub Desktop.
| <?php if(!defined('ABSPATH')) { die(); } // Include in all php files, to prevent direct execution | |
| /** | |
| * Plugin Name: WP Geo Query | |
| * Plugin URI: https://gschoppe.com/wordpress/geo-searches/ | |
| * Description: Adds location search support to WP_Query, making it easy to create completely custom "Find Location" pages. | |
| * Author: Greg Schoppe | |
| * Author URI: https://gschoppe.com | |
| * Version: 1.0.0 | |
| **/ | |
| if( !class_exists('GJSGeoQuery') ) { | |
| class GJSGeoQuery { | |
| public static function Instance() { | |
| static $instance = null; | |
| if ($instance === null) { | |
| $instance = new self(); | |
| } | |
| return $instance; | |
| } | |
| private function __construct() { | |
| add_filter( 'posts_fields' , array( $this, 'posts_fields' ), 10, 2 ); | |
| add_filter( 'posts_join' , array( $this, 'posts_join' ), 10, 2 ); | |
| add_filter( 'posts_where' , array( $this, 'posts_where' ), 10, 2 ); | |
| add_filter( 'posts_orderby', array( $this, 'posts_orderby' ), 10, 2 ); | |
| } | |
| // add a calculated "distance" parameter to the sql query, using a haversine formula | |
| public function posts_fields( $sql, $query ) { | |
| global $wpdb; | |
| $geo_query = $query->get('geo_query'); | |
| if( $geo_query ) { | |
| if( $sql ) { | |
| $sql .= ', '; | |
| } | |
| $sql .= $this->haversine_term( $geo_query ) . " AS geo_query_distance"; | |
| } | |
| return $sql; | |
| } | |
| public function posts_join( $sql, $query ) { | |
| global $wpdb; | |
| $geo_query = $query->get('geo_query'); | |
| if( $geo_query ) { | |
| if( $sql ) { | |
| $sql .= ' '; | |
| } | |
| $sql .= "INNER JOIN " . $wpdb->prefix . "postmeta AS geo_query_lat ON ( " . $wpdb->prefix . "posts.ID = geo_query_lat.post_id ) "; | |
| $sql .= "INNER JOIN " . $wpdb->prefix . "postmeta AS geo_query_lng ON ( " . $wpdb->prefix . "posts.ID = geo_query_lng.post_id ) "; | |
| } | |
| return $sql; | |
| } | |
| // match on the right metafields, and filter by distance | |
| public function posts_where( $sql, $query ) { | |
| global $wpdb; | |
| $geo_query = $query->get('geo_query'); | |
| if( $geo_query ) { | |
| $lat_field = 'latitude'; | |
| if( !empty( $geo_query['lat_field'] ) ) { | |
| $lat_field = $geo_query['lat_field']; | |
| } | |
| $lng_field = 'longitude'; | |
| if( !empty( $geo_query['lng_field'] ) ) { | |
| $lng_field = $geo_query['lng_field']; | |
| } | |
| $distance = 20; | |
| if( isset( $geo_query['distance'] ) ) { | |
| $distance = $geo_query['distance']; | |
| } | |
| if( $sql ) { | |
| $sql .= " AND "; | |
| } | |
| $haversine = $this->haversine_term( $geo_query ); | |
| $new_sql = "( geo_query_lat.meta_key = %s AND geo_query_lng.meta_key = %s AND " . $haversine . " <= %f )"; | |
| $sql .= $wpdb->prepare( $new_sql, $lat_field, $lng_field, $distance ); | |
| } | |
| return $sql; | |
| } | |
| // handle ordering | |
| public function posts_orderby( $sql, $query ) { | |
| $geo_query = $query->get('geo_query'); | |
| if( $geo_query ) { | |
| $orderby = $query->get('orderby'); | |
| $order = $query->get('order'); | |
| if( $orderby == 'distance' ) { | |
| if( !$order ) { | |
| $order = 'ASC'; | |
| } | |
| $sql = 'geo_query_distance ' . $order; | |
| } | |
| } | |
| return $sql; | |
| } | |
| public static function the_distance( $post_obj = null, $round = false ) { | |
| echo self::get_the_distance( $post_obj, $round ); | |
| } | |
| public static function get_the_distance( $post_obj = null, $round = false ) { | |
| global $post; | |
| if( !$post_obj ) { | |
| $post_obj = $post; | |
| } | |
| if( property_exists( $post_obj, 'geo_query_distance' ) ) { | |
| $distance = $post_obj->geo_query_distance; | |
| if( $round !== false ) { | |
| $distance = round( $distance, $round ); | |
| } | |
| return $distance; | |
| } | |
| return false; | |
| } | |
| private function haversine_term( $geo_query ) { | |
| global $wpdb; | |
| $units = "miles"; | |
| if( !empty( $geo_query['units'] ) ) { | |
| $units = strtolower( $geo_query['units'] ); | |
| } | |
| $radius = 3959; | |
| if( in_array( $units, array( 'km', 'kilometers' ) ) ) { | |
| $radius = 6371; | |
| } | |
| $lat_field = "geo_query_lat.meta_value"; | |
| $lng_field = "geo_query_lng.meta_value"; | |
| $lat = 0; | |
| $lng = 0; | |
| if( isset( $geo_query['latitude'] ) ) { | |
| $lat = $geo_query['latitude' ]; | |
| } | |
| if( isset( $geo_query['longitude'] ) ) { | |
| $lng = $geo_query['longitude']; | |
| } | |
| $haversine = "( " . $radius . " * "; | |
| $haversine .= "acos( cos( radians(%f) ) * cos( radians( " . $lat_field . " ) ) * "; | |
| $haversine .= "cos( radians( " . $lng_field . " ) - radians(%f) ) + "; | |
| $haversine .= "sin( radians(%f) ) * sin( radians( " . $lat_field . " ) ) ) "; | |
| $haversine .= ")"; | |
| $haversine = $wpdb->prepare( $haversine, array( $lat, $lng, $lat ) ); | |
| return $haversine; | |
| } | |
| } | |
| GJSGeoQuery::Instance(); | |
| } | |
| if( !function_exists( 'the_distance' ) ) { | |
| function the_distance( $post_obj = null, $round = false ) { | |
| GJSGeoQuery::the_distance( $post_obj, $round ); | |
| } | |
| } | |
| if( !function_exists( 'get_the_distance' ) ) { | |
| function get_the_distance( $post_obj = null, $round = false ) { | |
| return GJSGeoQuery::get_the_distance( $post_obj, $round ); | |
| } | |
| } |
| <?php | |
| // pre get posts filter | |
| add_filter('pre_get_posts','better_editions_archive'); | |
| function better_editions_archive( $query ) { | |
| if( $query->query['post_type'] == 'community_post' ){ | |
| /*$meta_query = array( | |
| array( | |
| 'lat_clause' => array( | |
| 'key' => 'lat', | |
| 'compare' => 'EXISTS' | |
| ) | |
| ), | |
| array( | |
| 'lng_clause' => array( | |
| 'key' => 'lng', | |
| 'compare' => 'EXISTS' | |
| ) | |
| ) | |
| ); | |
| $query->set('meta_query', $meta_query); | |
| $query->set('orderby', array('lat_clause' => 'ASC', 'lng_clause' => 'ASC'));*/ | |
| $query->set('geo_query', array( | |
| 'lat_field' => 'lat',// this is the name of the meta field storing latitude | |
| 'lng_field' => 'lng', // this is the name of the meta field storing longitude | |
| 'latitude' => get_user_current_location('lat'), // this is the latitude of the point we are getting distance from | |
| 'longitude' => get_user_current_location('lng'),// this is the longitude of the point we are getting distance from | |
| 'distance' => 30,// this is the maximum distance to search | |
| 'units' => 'km'// this supports options: miles, mi, kilometers, km | |
| )); | |
| $query->set( 'orderby', 'distance' ); | |
| $query->set('order', 'ASC'); | |
| //$query->set('orderby', array('lat_clause' => 'ASC', 'lng_clause' => 'ASC')); | |
| } | |
| print_r('<pre>'); | |
| //print_r( $query ); | |
| print_r('</pre>'); | |
| return $query; | |
| } | |
| //WP QUERY | |
| $query = new WP_Query(array( | |
| 'post_type' => 'community_post', | |
| 'geo_query' => array( | |
| 'lat_field' => 'lat',// this is the name of the meta field storing latitude | |
| 'lng_field' => 'lng', // this is the name of the meta field storing longitude | |
| 'latitude' => get_user_current_location('lat'), // this is the latitude of the point we are getting distance from | |
| 'longitude' => get_user_current_location('lng'),// this is the longitude of the point we are getting distance from | |
| 'distance' => 30,// this is the maximum distance to search | |
| 'units' => 'km'// this supports options: miles, mi, kilometers, km | |
| ), | |
| 'orderby' => 'distance', // this tells WP Query to sort by distance | |
| 'order' => 'ASC' | |
| )); |
This is great, thank you! The only issue I'm hitting is that get_the_distance() doesn't seem to work on the main archive queries, I can only get it to work in a secondary query via WP_Query. Is anyone getting it to work by hooking into the blog or CPT archive?
@JiveDig you can check your pre_get_posts hook/action. Pls check if you're getting correct distance in get_the_distance() function.
When the geo query is run via pre_get_posts, get_the_distance() returns early because property_exists( $post_obj, 'geo_query_distance' ) is false. The correct posts are shown though, only the distance is empty.
My pre_get_posts filter has this:
// Set geo query.
$query->set( 'orderby', 'distance' );
$query->set( 'order', 'ASC' );
$query->set( 'geo_query',
[
'lat_field' => 'location_lat',
'lng_field' => 'location_lng',
'latitude' => $lat,
'longitude' => $lng,
'distance' => $dist, // @int The maximum distance to search.
'units' => 'miles', // Supports options: miles, mi, kilometers, km.
]
);
Got it. The issue was that I was passing get_post( get_the_ID() ), which doesn't have the geo_query_distance property. I switched to the global $post object and now it works.
Thank you!