Quick and easy class created to modify the WP_Query on specific calls and pull in all posts within a radius from a given latitude and longitude.
Last active
September 10, 2024 14:47
-
-
Save jackabox/a9c4543fd7001a6f387df7166a0cf491 to your computer and use it in GitHub Desktop.
WordPress Geo Location Query
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$locations = new WP_Query_Geo([ | |
'post_status' => 'publish', | |
'post_type' => 'ash_locations', // cpt with locations stored | |
'posts_per_page' => -1, | |
'lat' => $lat, // pass in latitude | |
'lng' => $lng, // pass in longitude | |
'distance' => 10 // distance to find properties in | |
]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if( ! class_exists( 'WP_Query_Geo' ) ) { | |
class WP_Query_Geo extends WP_Query | |
{ | |
private $lat = NULL; | |
private $lng = NULL; | |
private $dist = NULL; | |
function __construct($args = []) | |
{ | |
if( !empty( $args['lat'] ) ) | |
$this->lat = $args['lat']; | |
if( !empty( $args['lng'] ) ) | |
$this->lng = $args['lng']; | |
if( !empty( $args['distance'] ) ) | |
$this->dist = $args['distance']; | |
if( !empty( $args['lat'] ) ) { | |
add_filter( 'posts_fields', [$this, 'posts_fields'], 10, 2 ); | |
add_filter( 'posts_join', [$this, 'posts_join'], 10, 2 ); | |
add_filter( 'posts_where', [$this, 'posts_where'], 10, 2 ); | |
add_filter( 'posts_orderby', [$this, 'posts_orderby'], 10, 2 ); | |
} | |
unset( $args['lat'], $args['lng'], $args['distance'] ); | |
parent::query($args); | |
# remove the filters again at the end (Resets for normal wp queries) | |
$this->remove_filters(); | |
} | |
/** | |
* Selects the distance from a haversine formula | |
*/ | |
public function posts_fields($fields) | |
{ | |
global $wpdb; | |
$fields .= sprintf(", ( 3959 * acos( | |
cos( radians( %s ) ) * | |
cos( radians( lat.meta_value ) ) * | |
cos( radians( lng.meta_value ) - radians( %s ) ) + | |
sin( radians( %s ) ) * | |
sin( radians( lat.meta_value ) ) | |
) ) AS distance ", $this->lat, $this->lng, $this->lat); | |
$fields .= ", lat.meta_value AS latitude "; | |
$fields .= ", lng.meta_value AS longitude "; | |
return $fields; | |
} // END public function posts_join($join, $query) | |
/** | |
* Makes joins as necessary in order to select lat/long metadata | |
*/ | |
public function posts_join($join, $query) | |
{ | |
global $wpdb; | |
$join .= " INNER JOIN {$wpdb->postmeta} AS lat ON {$wpdb->posts}.ID = lat.post_id "; | |
$join .= " INNER JOIN {$wpdb->postmeta} AS lng ON {$wpdb->posts}.ID = lng.post_id "; | |
return $join; | |
} // END public function posts_join($join, $query) | |
/** | |
* Adds where clauses to compliment joins | |
*/ | |
public function posts_where($where) | |
{ | |
$where .= ' AND lat.meta_key="ash_locations_location_latitude" '; | |
$where .= ' AND lng.meta_key="ash_locations_location_longitude" '; | |
$where .= " HAVING distance < {$this->dist}"; | |
return $where; | |
} // END public function posts_where($where) | |
/** | |
* order posts by distance, then any other term | |
* @param string $orderby | |
* @return string | |
*/ | |
public function posts_orderby($orderby) | |
{ | |
$orderby = " distance ASC, " . $orderby; | |
return $orderby; | |
} // END public function posts_orderby($orderby) | |
/** | |
* remove the filters from the query (this ensures we can keep our other queries clean) | |
* @return null | |
*/ | |
public function remove_filters() | |
{ | |
remove_filter( 'posts_fields', [$this, 'posts_fields'], 10, 2 ); | |
remove_filter( 'posts_join', [$this, 'posts_join'], 10, 2 ); | |
remove_filter( 'posts_where', [$this, 'posts_where'], 10, 2 ); | |
remove_filter( 'posts_orderby', [$this, 'posts_orderby'], 10, 2 ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This looks cool, thanks for sharing! Confirming that this class assumes the lat/lng is stored as post_meta via
ash_locations_location_latitude
andash_locations_location_longitude
right?