Skip to content
You are reading the docs for ShopClass 7.1.0.dev1, still in development. Read the stable docs.

Distance search in themes

Distance search lets visitors find listings within a distance of a city or of where they are. Site owners switch it on in Settings → General. When it is off, none of this shows and the fields below are ignored.

A search form sends these, as GET fields:

Field Value
iRadius 5, 10, 25, 50 or 100, in the site’s unit. Other numbers snap to the nearest.
sLat, sLng The visitor’s position, rounded to two decimals.
sOrder=distance Nearest first.

With iRadius and no sLat/sLng, the radius is measured from the centre of the one city, or else the one region, in the search. The radius then replaces that city filter.

Core adds osc_search_distance_field() to the search_form action. It prints a <fieldset class="osc-distance" data-osc-distance> with the radius list and a Use my location button (.osc-near-me, data-osc-near-me). Core ships no CSS for it, so style those classes. The button needs osc-ui-common, which the field enqueues, and it must sit inside the search <form>.

To draw it somewhere else, take it off the action and call it yourself:

osc_remove_hook('search_form', 'osc_search_distance_field');
// later, in your sidebar
if (osc_geo_search_enabled()) {
osc_search_distance_field();
}

To draw your own markup, use osc_search_radius_options() for the choices, osc_search_radius() for the current one and osc_search_near() for the point in use.

osc_list_orders() includes Nearest first when distance search is on. A theme with its own sort menu adds sOrder=distance&iOrderType=asc. With no distance picked, nearest first keeps to the largest one.

On a result, osc_item_distance() is the distance in whole units of the site, or null outside a distance search. osc_distance_label() turns a number into text such as “25 km” or “3 miles”.

Core sets each listing’s search point when a listing is saved through core. A plugin that writes t_item_location itself leaves the listing out of distance search until the weekly job sets its point.

With no provider, a listing searches from its city or region centre. A geocode provider plugin finds the exact position from the listing’s street address. The owner picks it in Settings → General → Maps → Address lookup.

Core looks the address up from a job after the listing is saved, only when the listing has a street and no position. With no provider picked, nothing is looked up; the map’s own key is not used for it. Each answer is kept in the key-value store for as long as the provider’s cacheTtl() allows, at most a year; an address with no match is kept for a day. A provider that throws a RuntimeException fails the job, and the queue tries again later. A provider whose request allowance is used up throws mindstellar\location\GeocodeLimitReached($message, $seconds) instead: the job waits that long and tries again, with no failure counted.

ListingGeocode::queueExisting() queues the lookup of every enabled listing that has a street and no position. One job adds them to the queue 50 at a time, and adds more only once those have run.

A provider plugin implements mindstellar\location\GeocodeProvider and registers it on register_geocode_providers (7.0+):

osc_add_hook('register_geocode_providers', static function ($registry): void {
$registry->register(new MyProvider());
});

geocode() returns [lat, lng] or null for no match. attribution() returns the credit the service requires; themes echo osc_geocode_attribution() as it is, without escaping. Other code can look an address up with osc_geocode($address, $countryCode). The geocode_address filter can rewrite an address first, and geocode_coordinates can change or drop an answer. geocode_coordinates runs only on a cache miss, so a cached answer skips it.

A theme that calls osc_geocode() on visitor input must cap or throttle it: each new string is a paid call to the provider.