Hook: mam_specials_get_post_lat

Signature

apply_filters( 'mam_specials_get_post_lat', mixed $lat, int $parent );
Parameter Type Description
$lat mixed The latitude resolved by reading lat meta from the parent listing. May be a float, a numeric string, an empty string, or false.
$parent int The parent listing’s post ID — the post the special offer’s special-offer-parent meta points at.

Returns: mixed — the latitude to use. Coerced to float by distance_between(). You must return it.


Purpose

Provides a per-parent override for the latitude used when computing whether an offer is “near” the requesting device. The plugin always sources offer coordinates from the offer’s parent listing, never from the offer itself, because offers don’t have their own location — they live where the venue lives. By default the parent’s coordinates come from lat post meta, which is GeoDirectory’s standard storage location.

This filter exists because not every parent post stores its coordinates that way. WooCommerce products don’t have a lat meta. Custom post types built on a different geo plugin may use different keys. And some apps want to relocate an offer’s apparent position (e.g., for a pop-up at a different venue than the parent).

For every read, the plugin calls a paired hook — mam_specials_get_post_lon — for longitude. They run side by side; treat them as one logical override.


When it runs

Twice per offer per request:

  1. Inside mam_special_offers::get_data_for_app() (the nearby-offers feed). For every published offer, the parent’s lat is read, filtered, and used to compute the device-to-offer distance. The result decides whether the offer is included in the response.
  2. Inside mam_special_offers::get_post_content() (the per-offer detail builder). The same value is filtered again and stored as the offer’s lat field in the JSON payload.

Source pattern:

$parent = get_post_meta( $post->ID, 'special-offer-parent', true );
$lat    = apply_filters( 'mam_specials_get_post_lat', get_post_meta( $parent, 'lat', true ), $parent );
$lon    = apply_filters( 'mam_specials_get_post_lon', get_post_meta( $parent, 'lon', true ), $parent );

The filter is consulted on the nearby-offers list builder and on each offer’s detail JSON. Returning different values across the two paths is technically allowed but will produce confusing UX (offer appears nearby, but its detail screen places it elsewhere).


Default behavior

mam_specials_get_post_lat → get_post_meta( $parent, 'lat', true )

If the parent has no lat meta, the filter receives an empty string. floatval('') is 0.0, which puts the offer “off the coast of West Africa” for distance purposes. Any positive request radius will still include such offers if $req_lat and $req_lon are also zero — see Hook: mam_specials_get_post_radius for the request-side gotcha.


Examples

Source coordinates from WooCommerce product meta

A product-driven app where product posts store coordinates as _product_lat / _product_lon:

add_filter( 'mam_specials_get_post_lat', 'my_app_product_lat', 10, 2 );
add_filter( 'mam_specials_get_post_lon', 'my_app_product_lon', 10, 2 );

function my_app_product_lat( $lat, $parent ) {
    if ( 'product' !== get_post_type( $parent ) ) {
        return $lat;
    }
    return get_post_meta( $parent, '_product_lat', true );
}

function my_app_product_lon( $lon, $parent ) {
    if ( 'product' !== get_post_type( $parent ) ) {
        return $lon;
    }
    return get_post_meta( $parent, '_product_lon', true );
}

Relocate an offer to a pop-up venue

Sometimes an offer’s parent venue isn’t where the offer is actually redeemable — a brewery-tied pop-up at a partner restaurant, for example. Store the override on the offer:

add_filter( 'mam_specials_get_post_lat', 'my_app_popup_lat', 10, 2 );

function my_app_popup_lat( $lat, $parent ) {
    // The 'parent' arg is the listing, but we want to peek at the offer.
    // Look up the offer the plugin is currently processing — but this filter
    // doesn't pass it. So store the override on the parent instead.
    $popup_lat = get_post_meta( $parent, '_specials_popup_lat', true );
    return $popup_lat ?: $lat;
}

If you need per-offer overrides, store them on the parent indexed by offer ID, or on the offer with a callback that reads the global post inside the filter — but the latter is fragile.

Synthesize coordinates from a city name

If your data model stores city names rather than lat/lon on parent posts, geocode lazily and cache:

add_filter( 'mam_specials_get_post_lat', 'my_app_city_lat', 10, 2 );

function my_app_city_lat( $lat, $parent ) {
    if ( $lat ) {
        return $lat;
    }
    $city = get_post_meta( $parent, 'city', true );
    if ( ! $city ) {
        return 0;
    }
    $coords = my_app_geocode_city_cached( $city );
    return $coords['lat'] ?? 0;
}

Cache the geocode lookup. The filter runs twice per offer per request.


Gotchas

  • Companion hook is required. Overriding mam_specials_get_post_lat without overriding mam_specials_get_post_lon will produce an “offer with right latitude, wrong longitude” — distance calculations will be nonsense. Always pair the two filters.
  • $parent may be a string or integer. It comes from get_post_meta(), which returns the stored value as-is. The plugin doesn’t cast before passing. If you === compare against an int, you may miss matches.
  • No fallback to the offer’s own coordinates. Even though the offer JSON has _range_lat / _range_lon on it, those are only read inside get_post_content() after the parent-derived lat is set. Returning 0 from this filter will mean the distance check fails — the _range_* fields aren’t a fallback path.
  • Runs on every offer on every nearby-offers request. A naive lookup that hits the database for every parent post will cost N queries per request. Cache in object cache or a request-scoped static.
  • Returning a value that isn’t numeric breaks the distance math. floatval() is called downstream; non-numeric strings become 0.0, masking the source of the bug. Return a number or a numeric string.

Verification

This article was last verified against:

  • Plugin: mam-special-offers v2.1
  • Source: mam-special-offers/includes/content-class-special-offers.php (get_data_for_app, get_post_content)

Re-verify whenever the parent-coordinate source meta key (lat) changes, the companion hook (mam_specials_get_post_lon) is renamed or removed, the parent linkage meta key (special-offer-parent) changes, or the distance comparison switches to a different reference point than $_REQUEST['lat'] / $_REQUEST['lon'].


  • Plugin: mam-special-offers
  • Hook: mam_specials_get_post_radius
  • Hook: mam_special_offers_deals_allowed
  • Hook: mam_special_offers_skip_tab_bar_button

Metadata

Field Value
Article type Hook Reference
Plugin slug mam-special-offers
Applies to plugin version 2.1+
Category Extending MAM Suite
Hook type filter
Audience PHP developer
Last verified 2026-05-01
Contents

    Need Support?

    Can’t find the answer you’re looking for? Don’t worry we’re here to help!