Description
This filter allows you to switch between km and miles in the search results based on the visitor’s location.
You can use this to show the distance in miles for US-based visitors, and in kilometers for visitors from other parts of the world.
To use one unit for the whole site, you don’t need this filter. Set the Distance unit on Store Locator > Settings > Search. To use a different unit on one page, add the distance_unit attribute to the shortcode, for example [wpsl distance_unit="mi"], or select it in the Distance unit option of the Store Locator block.
The filter receives the unit from the setting, or from the shortcode attribute when that is set. The value the filter returns always wins over both. The unit is used in the search radius dropdown, in the search results, and to calculate the distance between the searched location and the stores.
Usage
The code below shows how to use the filter. It uses miles for visitors from the US, and kilometers for everyone else. When the country can’t be found, the unit from the setting or the shortcode attribute is used.
It calls the custom_get_user_country function to get the country code of the current visitor. You need all three parts of the code on this page. They’re explained in more detail in the Show the Distance in Miles for US based Visitors and in Kilometers for Everyone Else article.
| add_filter( 'wpsl_distance_unit', 'custom_distance_unit' ); | |
| /** | |
| * Use miles for visitors from the US, and kilometers for everyone else. | |
| * | |
| * @param string $distance_unit The distance unit from the settings. | |
| * @return string Either 'mi' or 'km'. | |
| */ | |
| function custom_distance_unit( $distance_unit ) { | |
| $user_country = custom_get_user_country(); | |
| // Keep the setting when the country is unknown. | |
| if ( ! $user_country ) { | |
| return $distance_unit; | |
| } | |
| return ( 'US' === $user_country ) ? 'mi' : 'km'; | |
| } |
The code below shows the custom_get_user_country function. If your site runs behind Cloudflare and Cloudflare adds the CF-IPCountry header, the country is read from that header. Otherwise the IP is sent to the ipstack API to find the country code. Add your ipstack API key between the quotes of $api_key = '';. The country is cached for 30 minutes.
| /** | |
| * Get the country code of the current visitor. | |
| * | |
| * @return string The country code, for example 'US', or an empty string. | |
| */ | |
| function custom_get_user_country() { | |
| // Cloudflare can add the visitor's country itself, then no API request is needed. | |
| if ( ! empty( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) { | |
| return strtoupper( sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) ); | |
| } | |
| // The API key from https://ipstack.com/. | |
| $api_key = ''; | |
| // How long the country is cached, in seconds ( 1800 = 30 minutes ). | |
| $cache_expires = 1800; | |
| $user_ip = custom_get_user_ip(); | |
| // Uncomment the line below to test it with a US based IP. | |
| // $user_ip = '54.239.17.6'; | |
| if ( ! $user_ip || ! $api_key ) { | |
| return ''; | |
| } | |
| $transient_name = 'custom_user_country_' . md5( $user_ip ); | |
| $user_country = get_transient( $transient_name ); | |
| if ( false === $user_country ) { | |
| $user_country = ''; | |
| $api_url = add_query_arg( | |
| [ | |
| 'access_key' => rawurlencode( $api_key ), | |
| 'fields' => 'country_code', | |
| ], | |
| 'http://api.ipstack.com/' . rawurlencode( $user_ip ) | |
| ); | |
| $response = wp_remote_get( $api_url ); | |
| if ( ! is_wp_error( $response ) ) { | |
| $user_location = json_decode( wp_remote_retrieve_body( $response ) ); | |
| if ( ! empty( $user_location->country_code ) ) { | |
| $user_country = $user_location->country_code; | |
| } | |
| } | |
| // Cache a failed lookup for 5 minutes, so the API isn't called on every page load. | |
| set_transient( $transient_name, $user_country, $user_country ? $cache_expires : 300 ); | |
| } | |
| return $user_country; | |
| } |
The last function grabs the IP from the current visitor.
| /** | |
| * Get the IP of the current visitor. | |
| * | |
| * @return string The IP, or an empty string if no valid IP is found. | |
| */ | |
| function custom_get_user_ip() { | |
| $headers = [ 'HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR' ]; | |
| foreach ( $headers as $header ) { | |
| if ( empty( $_SERVER[ $header ] ) ) { | |
| continue; | |
| } | |
| // X-Forwarded-For can hold a list of IPs, the first one is the visitor. | |
| $ips = array_map( 'trim', explode( ',', wp_unslash( $_SERVER[ $header ] ) ) ); | |
| if ( filter_var( $ips[0], FILTER_VALIDATE_IP ) ) { | |
| return $ips[0]; | |
| } | |
| } | |
| return ''; | |
| } |
Changelog
| Version | Description |
|---|---|
| 3.0 | Moved to includes/core/functions/functions-settings.php. New distance_unit shortcode attribute, also available on the Store Locator block. The filter receives the attribute value when it's set, and its return value overrides it. The search request now uses the unit that was set when the page loaded. |
| 2.2.8 | Introduced. |
Source File
This filter is located in /includes/core/functions/functions-settings.php.
Placement
The code needs to be placed in the functions.php file inside your active theme folder.