-
-
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!