针对访客进行地理位置定位简易版WordPress开发指南

针对访客进行地理位置定位简易版WordPress开发指南

现代网络应用需要高度智能的位置感知功能。电子商务平台需要计算国际运费,内容网站需要提供特定地区的新闻。地理位置定位可以将静态的 WordPress 网站转变为动态的个性化体验。本综合指南将探讨如何在 WordPress 中实现强大的访客地理位置定位功能。

了解地理定向和地理定位

位置感知应用程序以两个关键术语为中心:地理定向和地理定位。它们相互关联,但在 WordPress 工具包中各有不同的作用:

  • 地理定向(Geotargeting)指的是根据用户的地理位置(如国家、地区、城市等)来进行目标营销或内容分发的过程。常用于广告投放、内容推荐或市场推广中,以确保信息更符合特定地区用户的需求。
  • 地理定位(Geolocation)是通过技术手段(如GPS、IP地址、Wi-Fi等)确定设备或用户的物理地理位置的过程。常用于导航、位置服务、位置共享或emergency服务等场景。

Netflix 就有效地证明了这一点:当您出国旅行时,该流媒体服务会通过地理定位确定您当前所在的国家,然后通过地理定向调整您的内容库,以符合地区许可协议。这种组合既能创造无缝体验,又能保持与国际媒体版权的一致性。

Netflix 网站

Netflix 网站

检测访客位置的主要方法有两种:

  • 基于 IP 的位置检测依赖于将 IP 地址映射到地理区域的数据库。这种方法普遍适用,无需用户许可。大多数服务器端地理定位解决方案都使用这种方法。
  • 基于 GPS 的检测通过浏览器 API 访问设备的定位硬件。它能提供精确坐标,但需要用户明确同意。这种功能适用于需要精确定位的“附近的我”位置搜索或天气应用程序。

地理定位和地理定向是相互配合的,但在前者中,有多种技术可用于特定用例。二者都不“优于”对方–各自的优势超越了各自的适应性。

地理定位的商业案例

在任何网站上实施地理定位都能为企业和用户带来实实在在的好处。首先,个性化能提高参与度。如果您的网站能够为每个用户提供个性化体验,那么他们中的更多人可能会与您开展业务。

亚马逊就是一个可以让您获得回报的例子。它的购物网站可以根据您的地理位置显示每件产品的运输时间:

亚马逊产品页面显示手表的运输时间

亚马逊产品页面显示手表的运输时间

根据您所在的位置,您还可以看到亚马逊 Prime活动(如体育比赛)的相关时间:

野马队与印第安人队比赛的当地时间

亚马逊主页显示了野马队与印第安人队比赛的当地时间

你可以在网络上看到这种情况,比如天气网站会自动显示你当地的天气预报。无论采用哪种方式,个性化都能减少用户体验中的摩擦,并显著提高转换率。

监管合规性也取决于位置意识。欧洲的 GDPR加利福尼亚州的 CCPA 以及更多地区都规定了处理用户数据的具体要求。正确实施地理定位可帮助您满足针对每位访客的此类要求。

最后,本地化内容可以建立信任。研究表明,当价格以当地货币显示且运输信息清晰明了时,用户会更频繁地完成购买。例如,Common Sense Advisory 的一项研究表明,四分之三的消费者喜欢用母语购买产品。

如何在WordPress中集成地理定位功能

WordPress 灵活的架构意味着,如果您希望在网站上添加位置感知功能,可以采用多种方法。您对网站托管的选择、插件的使用、编码知识等都会影响到您的首选方法。不过,处理数据本身有几种方法。

在WordPress中处理地理数据

虽然 WordPress 核心包含很多基本功能,但其中并不包括内置地理定位功能。尽管 WordPress 内核支持以多种方式存储和处理位置数据,但它并没有记录位置数据的功能。

WordPress数据库

例如,WordPress 数据库可以使用自定义字段或专用位置表来存储坐标和位置数据。

它还可以处理基于位置的分类标准。这对于运行自定义位置数据库的商店定位器或房地产列表服务非常有效。不过,WordPress 默认并不存储任何地理位置数据,只是有能力这样做而已。

WordPress REST API

WordPress REST API 也支持处理地理位置数据,不过它不包括专用端点。您需要创建自己的端点扩展,以构建与外部服务或移动应用程序通信的位置感知应用程序。下面是一个例子:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
add_action('rest_api_init', function() {
// Create a custom namespace for your geolocation endpoints
register_rest_route('your-site-geo/v1', '/location', [
'methods' => 'GET',
'callback' => 'handle_location_request',
'permission_callback' => function() {
return true;
}
]);
});
function handle_location_request($request) {
// Access geolocation data
$location = [
'country' => $_SERVER['GEOIP_COUNTRY_CODE'] ?? null,
'city' => $_SERVER['GEOIP_CITY'] ?? null,
'latitude' => $_SERVER['GEOIP_LATITUDE'] ?? null,
'longitude' => $_SERVER['GEOIP_LONGITUDE'] ?? null
];
return new WP_REST_Response($location, 200);
}
add_action('rest_api_init', function() { // Create a custom namespace for your geolocation endpoints register_rest_route('your-site-geo/v1', '/location', [ 'methods' => 'GET', 'callback' => 'handle_location_request', 'permission_callback' => function() { return true; } ]); }); function handle_location_request($request) { // Access geolocation data $location = [ 'country' => $_SERVER['GEOIP_COUNTRY_CODE'] ?? null, 'city' => $_SERVER['GEOIP_CITY'] ?? null, 'latitude' => $_SERVER['GEOIP_LATITUDE'] ?? null, 'longitude' => $_SERVER['GEOIP_LONGITUDE'] ?? null ]; return new WP_REST_Response($location, 200); }
add_action('rest_api_init', function() {
// Create a custom namespace for your geolocation endpoints
register_rest_route('your-site-geo/v1', '/location', [
'methods' => 'GET',
'callback' => 'handle_location_request',
'permission_callback' => function() {
return true;
}
]);
});
function handle_location_request($request) {
// Access geolocation data
$location = [
'country' => $_SERVER['GEOIP_COUNTRY_CODE'] ?? null,
'city' => $_SERVER['GEOIP_CITY'] ?? null,
'latitude' => $_SERVER['GEOIP_LATITUDE'] ?? null,
'longitude' => $_SERVER['GEOIP_LONGITUDE'] ?? null
];
return new WP_REST_Response($location, 200);
}

这会在 /wp-json/your-site-geo/v1/location 创建一个新端点,为任何 API 消费者返回位置数据。

代码使用 your-site-geo 作为自定义命名空间。您的命名空间应符合您的具体需求,以避免与其他插件或自定义代码发生冲突。最好遵循 WordPress 命名空间指南:

  • 使用特定于供应商或软件包的前缀以避免冲突。
  • 包含版本号(如v1)。
  • 保持端点的特定性和集中性。

您还可以为现有端点注册位置数据:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
add_action('rest_api_init', function() {
register_rest_field('post', 'location_data', [
'get_callback' => function($post) {
return get_post_meta($post['id'], 'location_data', true);
},
'update_callback' => function($value, $post) {
update_post_meta($post->ID, 'location_data', $value);
},
'schema' => [
'type' => 'object',
'properties' => [
'latitude' => ['type' => 'number'],
'longitude' => ['type' => 'number'],
'country' => ['type' => 'string'],
'city' => ['type' => 'string']
]
]
]);
});
add_action('rest_api_init', function() { register_rest_field('post', 'location_data', [ 'get_callback' => function($post) { return get_post_meta($post['id'], 'location_data', true); }, 'update_callback' => function($value, $post) { update_post_meta($post->ID, 'location_data', $value); }, 'schema' => [ 'type' => 'object', 'properties' => [ 'latitude' => ['type' => 'number'], 'longitude' => ['type' => 'number'], 'country' => ['type' => 'string'], 'city' => ['type' => 'string'] ] ] ]); });
add_action('rest_api_init', function() {
register_rest_field('post', 'location_data', [
'get_callback' => function($post) {
return get_post_meta($post['id'], 'location_data', true);
},
'update_callback' => function($value, $post) {
update_post_meta($post->ID, 'location_data', $value);
},
'schema' => [
'type' => 'object',
'properties' => [
'latitude' => ['type' => 'number'],
'longitude' => ['type' => 'number'],
'country' => ['type' => 'string'],
'city' => ['type' => 'string']
]
]
]);
});

在许多情况下,REST API 将是您建立地理位置功能的首选,这意味着拥有这种灵活性将是一大福音。

自定义文章类型

您也可以在 WordPress 中使用自定义文章类型。如果是这样,您创建的新内容类型就可以包含位置元数据。这样,您就可以使用地理信息来组织内容,而无需修改复杂的数据库。

第一步是在 WordPress 中注册文章类型:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
register_post_type('store_location', [
'public' => true,
'label' => 'Store Locations',
'supports' => [
'title',
'editor',
'Custom-fields' // Enables custom fields and meta
]
]);
register_post_type('store_location', [ 'public' => true, 'label' => 'Store Locations', 'supports' => [ 'title', 'editor', 'Custom-fields' // Enables custom fields and meta ] ]);
register_post_type('store_location', [
'public' => true,
'label' => 'Store Locations',
'supports' => [
'title',
'editor',
'Custom-fields'  // Enables custom fields and meta
]
]);

您还应创建一个自定义元方框,用于存储所收集的坐标,并为其生成 HTML:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?php
add_action('add_meta_boxes', function() {
add_meta_box(
'store_location_coordinates',
'Store Coordinates',
'render_location_meta_box',
'Store_location' // The post type where this meta box should appear
);
});
function render_location_meta_box($post) {
// Get existing coordinates if they exist
$latitude = get_post_meta($post->ID, 'latitude', true);
$longitude = get_post_meta($post->ID, 'longitude', true);
// Output the form fields
?>
<label>Latitude:
<input type="number"
step="any"
name="latitude"
value="<?php echo esc_attr($latitude); ?>">
</label>
<label>Longitude:
<input type="number"
step="any"
name="longitude"
value="<?php echo esc_attr($longitude); ?>">
</label>
<?php
}
<?php add_action('add_meta_boxes', function() { add_meta_box( 'store_location_coordinates', 'Store Coordinates', 'render_location_meta_box', 'Store_location' // The post type where this meta box should appear ); }); function render_location_meta_box($post) { // Get existing coordinates if they exist $latitude = get_post_meta($post->ID, 'latitude', true); $longitude = get_post_meta($post->ID, 'longitude', true); // Output the form fields ?> <label>Latitude: <input type="number" step="any" name="latitude" value="<?php echo esc_attr($latitude); ?>"> </label> <label>Longitude: <input type="number" step="any" name="longitude" value="<?php echo esc_attr($longitude); ?>"> </label> <?php }
<?php
add_action('add_meta_boxes', function() {
add_meta_box(
'store_location_coordinates',
'Store Coordinates',
'render_location_meta_box',
'Store_location'  // The post type where this meta box should appear
);
});
function render_location_meta_box($post) {
// Get existing coordinates if they exist
$latitude = get_post_meta($post->ID, 'latitude', true);
$longitude = get_post_meta($post->ID, 'longitude', true);    
// Output the form fields
?>
<label>Latitude:
<input type="number" 
step="any" 
name="latitude" 
value="<?php echo esc_attr($latitude); ?>">
</label>
<label>Longitude:
<input type="number" 
step="any" 
name="longitude" 
value="<?php echo esc_attr($longitude); ?>">
</label>
<?php
}

关键是在 WordPress 中发布或保存文章后保存位置数据:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
add_action('save_post_store_location', function($post_id) {
// Verify if this is an autosave
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// Save latitude if it exists
if (isset($_POST['latitude'])) {
update_post_meta(
$post_id,
'latitude',
sanitize_text_field($_POST['latitude'])
);
}
// Save longitude if it exists
if (isset($_POST['longitude'])) {
update_post_meta(
$post_id,
'longitude',
sanitize_text_field($_POST['longitude'])
);
}
});
add_action('save_post_store_location', function($post_id) { // Verify if this is an autosave if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } // Save latitude if it exists if (isset($_POST['latitude'])) { update_post_meta( $post_id, 'latitude', sanitize_text_field($_POST['latitude']) ); } // Save longitude if it exists if (isset($_POST['longitude'])) { update_post_meta( $post_id, 'longitude', sanitize_text_field($_POST['longitude']) ); } });
add_action('save_post_store_location', function($post_id) {
// Verify if this is an autosave
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}    
// Save latitude if it exists
if (isset($_POST['latitude'])) {
update_post_meta(
$post_id,
'latitude',
sanitize_text_field($_POST['latitude'])
);
}    
// Save longitude if it exists
if (isset($_POST['longitude'])) {
update_post_meta(
$post_id,
'longitude',
sanitize_text_field($_POST['longitude'])
);
}
});

创建一个函数来抓取附近的任何地点可以有多种形式。下面是一个粗略的示例,(理论上)可以获取一个地点的经纬度:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function get_nearby_locations($lat, $lng, $radius = 10) {
$locations = get_posts([
'post_type' => 'store_location',
'posts_per_page' => -1
]);
$nearby = array_filter($locations, function($location) use ($lat, $lng, $radius) {
$store_lat = get_post_meta($location->ID, 'latitude', true);
$store_lng = get_post_meta($location->ID, 'longitude', true);
return calculate_distance($lat, $lng, $store_lat, $store_lng) <= $radius;
});
return $nearby;
}
function get_nearby_locations($lat, $lng, $radius = 10) { $locations = get_posts([ 'post_type' => 'store_location', 'posts_per_page' => -1 ]); $nearby = array_filter($locations, function($location) use ($lat, $lng, $radius) { $store_lat = get_post_meta($location->ID, 'latitude', true); $store_lng = get_post_meta($location->ID, 'longitude', true); return calculate_distance($lat, $lng, $store_lat, $store_lng) <= $radius; }); return $nearby; }
function get_nearby_locations($lat, $lng, $radius = 10) {
$locations = get_posts([
'post_type' => 'store_location',
'posts_per_page' => -1
]);
$nearby = array_filter($locations, function($location) use ($lat, $lng, $radius) {
$store_lat = get_post_meta($location->ID, 'latitude', true);
$store_lng = get_post_meta($location->ID, 'longitude', true);
return calculate_distance($lat, $lng, $store_lat, $store_lng) <= $radius;
});
return $nearby;
}

使用这些选择中的任何一种都取决于您的具体需求。例如,使用自定义字段获取简单的位置元数据,或扩展 REST API 以实现无头实施。对于以位置为中心的内容,最好使用自定义帖子类型。

地理位置定位的插件方法

WordPress 支持几乎所有的插件,也有许多解决方案可以为网站添加位置感知。例如,If-So GeolocationGeolocation IP Detection 有很好的评价、定期更新和健全的支持系统:

Geolocation IP Detection 插件

Geolocation IP Detection 插件

这些插件使用熟悉的格式,以简单的封装提供了大量典型功能。当你需要实施地理定位时,插件可以为你带来很多好处:

  • 可以快速部署,无需自定义代码即可快速实施。
  • 您无需进行维护,因为这是插件开发人员的工作(以及更新)。
  • 如果需要,通常会有社区支持和文档来提供帮助。

对于您想要进行的开发类型,插件可能不是最佳选择。首先,您的功能依赖于插件的质量。如果您提供定制的 WordPress 产品,这可能也不是您想要的合作方式。每个插件都有自己的位置数据库,其质量可能与其他功能一样参差不齐。

对于需要在现有网站上添加定位功能的最终用户或网站所有者来说,插件是非常好的选择。但是,与其他插件冲突的可能性、额外的服务器负载以及自定义实施的有限灵活性意味着你可能需要一个更强大的选项。

WordPress地理定位在现实中的应用

您不需要走很远就能找到 WordPress 产品在其代码库中使用地理定位的例子。例如,WooCommerce 就内置了用于税收计算和运输规则的地理定位功能。

WooCommerce 中的地理位置选项

WooCommerce 中的地理位置选项

它使用 MaxMind’s GeoIP2 database 自动检测客户位置,这有助于确保从首次加载页面开始就提供准确的定价和送货选项。

其他插件也支持地理定位。像 Gravity Forms 的 Gravity Geolocation 插件这样的表单插件就是一个典型的使用案例。它可以让用户在表单中输入自己的位置,然后 Gravity Forms 会分配一组坐标和其他相关数据。

这对于潜在客户生成和服务请求表单尤其有价值。更重要的是,您可以根据具体的使用情况灵活调整对位置感知的需求。

服务器原生地理位置定位功能

如果你技术过硬,对服务器开发有开发经验。不需要奢华而笨拙的解决方案来实现地理定位。可以通过服务器的 NGINX 模块和地理IP数据库来实现强大的位置检测功能,无需额外的 WordPress 插件。

了解服务器级别的地理定位系统

服务器级别的地理定位实施利用了两项强大的技术:

  • NGINX 的本地地理定位模块提供高效的服务器级位置检测。
  • MaxMind 的 GeoIP2 数据库可确保准确的最新位置映射。

与基于插件或代码的解决方案相比,这种集成具有多项优势:

  • 由于位置检测是在服务器级别进行的,因此与其他方法相比,对性能的影响最小。
  • 技术人员只要对功能进行定期维护,这意味着您可以获得最新的地理位置。
  • 您的网站可以获得可靠的位置感知和检测,而无需 JavaScript 或浏览器权限。

可用的位置数据

WordPress 使用 PHP,IP 地理定位工具会将一些以位置为中心的信息传递给 PHP 变量 $_SERVER

您可以使用许多端点和变量来返回不同的数据集:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Basic geographical data
$country_code = $_SERVER['GEOIP_COUNTRY_CODE']; // Two-letter country code (such as "US" or "GB")
$country_name = $_SERVER['GEOIP_COUNTRY_NAME']; // Full country name
$region = $_SERVER['GEOIP_REGION']; // State/province code
$city = $_SERVER['GEOIP_CITY']; // City name
$postal_code = $_SERVER['GEOIP_POSTAL_CODE']; // Local postal or ZIP code
// Precise location data
$latitude = $_SERVER['GEOIP_LATITUDE']; // Decimal latitude
$longitude = $_SERVER['GEOIP_LONGITUDE']; // Decimal longitude
// Basic geographical data $country_code = $_SERVER['GEOIP_COUNTRY_CODE']; // Two-letter country code (such as "US" or "GB") $country_name = $_SERVER['GEOIP_COUNTRY_NAME']; // Full country name $region = $_SERVER['GEOIP_REGION']; // State/province code $city = $_SERVER['GEOIP_CITY']; // City name $postal_code = $_SERVER['GEOIP_POSTAL_CODE']; // Local postal or ZIP code // Precise location data $latitude = $_SERVER['GEOIP_LATITUDE']; // Decimal latitude $longitude = $_SERVER['GEOIP_LONGITUDE']; // Decimal longitude
// Basic geographical data
$country_code = $_SERVER['GEOIP_COUNTRY_CODE']; // Two-letter country code (such as "US" or "GB")
$country_name = $_SERVER['GEOIP_COUNTRY_NAME']; // Full country name
$region = $_SERVER['GEOIP_REGION'];             // State/province code
$city = $_SERVER['GEOIP_CITY'];                 // City name
$postal_code = $_SERVER['GEOIP_POSTAL_CODE'];   // Local postal or ZIP code
// Precise location data
$latitude = $_SERVER['GEOIP_LATITUDE'];         // Decimal latitude
$longitude = $_SERVER['GEOIP_LONGITUDE'];       // Decimal longitude

还有更多变量可以使用,例如各种国家和城市代码格式。无论如何,所有暴露的变量都能让您根据 IP 地理位置功能编写自定义 PHP 代码。

构建位置感知应用程序:实例

通过 NGINX 和对地理位置的实现,您几乎可以使用所有的工具和功能。这意味着您有足够的空间使用代码片段和服务器内置功能来建立一个满足您需求的解决方案。

基于地理位置的重定向

如果要使用代码进行这样的设置,就需要做大量的工作:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class GeographicRedirects {
public function __construct() {
add_action('template_redirect', [$this, 'handle_redirects']);
}
public function handle_redirects() {
$country_code = $_SERVER['GEOIP_COUNTRY_CODE'] ?? null;
if ($country_code) {
$redirect_url = $this->get_redirect_url($country_code);
if ($redirect_url) {
wp_redirect($redirect_url, 301);
exit;
}
}
}
private function get_redirect_url($country_code) {
$redirects = [
'DE' => 'https://de.example.com',
'FR' => 'https://fr.example.com',
'ES' => 'https://es.example.com'
];
return $redirects[$country_code] ?? null;
}
}
new GeographicRedirects();
class GeographicRedirects { public function __construct() { add_action('template_redirect', [$this, 'handle_redirects']); } public function handle_redirects() { $country_code = $_SERVER['GEOIP_COUNTRY_CODE'] ?? null; if ($country_code) { $redirect_url = $this->get_redirect_url($country_code); if ($redirect_url) { wp_redirect($redirect_url, 301); exit; } } } private function get_redirect_url($country_code) { $redirects = [ 'DE' => 'https://de.example.com', 'FR' => 'https://fr.example.com', 'ES' => 'https://es.example.com' ]; return $redirects[$country_code] ?? null; } } new GeographicRedirects();
class GeographicRedirects {
public function __construct() {
add_action('template_redirect', [$this, 'handle_redirects']);
}
public function handle_redirects() {
$country_code = $_SERVER['GEOIP_COUNTRY_CODE'] ?? null;
if ($country_code) {
$redirect_url = $this->get_redirect_url($country_code);
if ($redirect_url) {
wp_redirect($redirect_url, 301);
exit;
}
}
}
private function get_redirect_url($country_code) {
$redirects = [
'DE' => 'https://de.example.com',
'FR' => 'https://fr.example.com',
'ES' => 'https://es.example.com'
];
return $redirects[$country_code] ?? null;
}
}
new GeographicRedirects();

互动地图集成

动态商店定位器将显示访问者的位置和附近的任何商店。如果您有多家分店,尤其是分店分布在广阔的区域内,这将是您的理想选择:

宜家荷兰网站上的商店定位器

宜家荷兰网站上的商店定位器

您可以使用地理定位变量和谷歌地图 API 创建类似的互动体验。为此,您需要一个 Google Maps API 密钥和对 Google Maps JavaScript API 的基本了解。此外,您还需要运行的每个商店的位置数据。在这种情况下,数据可以来自自定义帖子类型或数据库。

在实施过程中,通常的做法是将代码添加到主题的 functions.php 文件或自定义插件中。设置好 Google Maps API 密钥后,就可以设置店铺位置数据结构了:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Register the store locations post type
add_action('init', function() {
register_post_type('store_location', [
'public' => true,
'label' => 'Store Locations',
'supports' => ['title', 'editor', 'custom-fields'],
'show_in_rest' => true
]);
// Register custom fields for location data
register_meta('post', 'latitude', [
'type' => 'number',
'single' => true,
'show_in_rest' => true
]);
register_meta('post', 'longitude', [
'type' => 'number',
'single' => true,
'show_in_rest' => true
]);
});
// Register the store locations post type add_action('init', function() { register_post_type('store_location', [ 'public' => true, 'label' => 'Store Locations', 'supports' => ['title', 'editor', 'custom-fields'], 'show_in_rest' => true ]); // Register custom fields for location data register_meta('post', 'latitude', [ 'type' => 'number', 'single' => true, 'show_in_rest' => true ]); register_meta('post', 'longitude', [ 'type' => 'number', 'single' => true, 'show_in_rest' => true ]); });
// Register the store locations post type
add_action('init', function() {
register_post_type('store_location', [
'public' => true,
'label' => 'Store Locations',
'supports' => ['title', 'editor', 'custom-fields'],
'show_in_rest' => true
]);
// Register custom fields for location data
register_meta('post', 'latitude', [
'type' => 'number',
'single' => true,
'show_in_rest' => true
]);
register_meta('post', 'longitude', [
'type' => 'number',
'single' => true,
'show_in_rest' => true
]);
});

要导入店铺位置,您可以使用 WordPress 管理界面、创建自定义文章类型、在自定义字段中添加纬度和经度、在内容中添加店铺详细信息,甚至进行程序导入。例如:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function import_store_locations($stores) {
foreach ($stores as $store) {
$post_id = wp_insert_post([
'post_type' => 'store_location',
'post_title' => sanitize_text_field($store['name']),
'post_status' => 'publish'
]);
if (!is_wp_error($post_id)) {
update_post_meta($post_id, 'latitude', floatval($store['lat']));
update_post_meta($post_id, 'longitude', floatval($store['lng']));
update_post_meta($post_id, 'address', sanitize_text_field($store['address']));
update_post_meta($post_id, 'phone', sanitize_text_field($store['phone']));
}
}
}
// Example usage:
$stores = [
[
'name' => 'Downtown Store',
'lat' => 40.7128,
'lng' => -74.0060,
'address' => '123 Main St',
'phone' => '(555) 123-4567'
]
// Add more stores...
];
import_store_locations($stores);
function import_store_locations($stores) { foreach ($stores as $store) { $post_id = wp_insert_post([ 'post_type' => 'store_location', 'post_title' => sanitize_text_field($store['name']), 'post_status' => 'publish' ]); if (!is_wp_error($post_id)) { update_post_meta($post_id, 'latitude', floatval($store['lat'])); update_post_meta($post_id, 'longitude', floatval($store['lng'])); update_post_meta($post_id, 'address', sanitize_text_field($store['address'])); update_post_meta($post_id, 'phone', sanitize_text_field($store['phone'])); } } } // Example usage: $stores = [ [ 'name' => 'Downtown Store', 'lat' => 40.7128, 'lng' => -74.0060, 'address' => '123 Main St', 'phone' => '(555) 123-4567' ] // Add more stores... ]; import_store_locations($stores);
function import_store_locations($stores) {
foreach ($stores as $store) {
$post_id = wp_insert_post([
'post_type' => 'store_location',
'post_title' => sanitize_text_field($store['name']),
'post_status' => 'publish'
]);
if (!is_wp_error($post_id)) {
update_post_meta($post_id, 'latitude', floatval($store['lat']));
update_post_meta($post_id, 'longitude', floatval($store['lng']));
update_post_meta($post_id, 'address', sanitize_text_field($store['address']));
update_post_meta($post_id, 'phone', sanitize_text_field($store['phone']));
}
}
}
// Example usage:
$stores = [
[
'name' => 'Downtown Store',
'lat' => 40.7128,
'lng' => -74.0060,
'address' => '123 Main St',
'phone' => '(555) 123-4567'
]
// Add more stores...
];
import_store_locations($stores);

执行定位器只需几行代码和公开的变量:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class StoreLocator {
private $visitor_location;
private $google_maps_key;
public function __construct($google_maps_key) {
$this->google_maps_key = $google_maps_key;
$this->visitor_location = $this->get_visitor_location();
add_action('wp_enqueue_scripts', [$this, 'enqueue_maps_scripts']);
}
private function get_visitor_location() {
// Use Kinsta's geolocation data
if (isset($_SERVER['GEOIP_LATITUDE'], $_SERVER['GEOIP_LONGITUDE'])) {
return [
'lat' => floatval($_SERVER['GEOIP_LATITUDE']),
'lng' => floatval($_SERVER['GEOIP_LONGITUDE'])
];
}
// Fallback to country center
if (isset($_SERVER['GEOIP_COUNTRY_CODE'])) {
return $this->get_country_center($_SERVER['GEOIP_COUNTRY_CODE']);
}
// Default to New York
return ['lat' => 40.7128, 'lng' => -74.0060];
}
private function get_nearby_stores($radius = 50) {
return get_posts([
'post_type' => 'store_location',
'posts_per_page' => 10,
'meta_query' => [
[
'key' => 'latitude',
'value' => [
$this->visitor_location['lat'] - ($radius / 111),
$this->visitor_location['lat'] + ($radius / 111)
],
'type' => 'NUMERIC',
'compare' => 'BETWEEN'
]
]
]);
}
class StoreLocator { private $visitor_location; private $google_maps_key; public function __construct($google_maps_key) { $this->google_maps_key = $google_maps_key; $this->visitor_location = $this->get_visitor_location(); add_action('wp_enqueue_scripts', [$this, 'enqueue_maps_scripts']); } private function get_visitor_location() { // Use Kinsta's geolocation data if (isset($_SERVER['GEOIP_LATITUDE'], $_SERVER['GEOIP_LONGITUDE'])) { return [ 'lat' => floatval($_SERVER['GEOIP_LATITUDE']), 'lng' => floatval($_SERVER['GEOIP_LONGITUDE']) ]; } // Fallback to country center if (isset($_SERVER['GEOIP_COUNTRY_CODE'])) { return $this->get_country_center($_SERVER['GEOIP_COUNTRY_CODE']); } // Default to New York return ['lat' => 40.7128, 'lng' => -74.0060]; } private function get_nearby_stores($radius = 50) { return get_posts([ 'post_type' => 'store_location', 'posts_per_page' => 10, 'meta_query' => [ [ 'key' => 'latitude', 'value' => [ $this->visitor_location['lat'] - ($radius / 111), $this->visitor_location['lat'] + ($radius / 111) ], 'type' => 'NUMERIC', 'compare' => 'BETWEEN' ] ] ]); } …
class StoreLocator {
private $visitor_location;
private $google_maps_key;
public function __construct($google_maps_key) {
$this->google_maps_key = $google_maps_key;
$this->visitor_location = $this->get_visitor_location();        
add_action('wp_enqueue_scripts', [$this, 'enqueue_maps_scripts']);
}
private function get_visitor_location() {
// Use Kinsta's geolocation data
if (isset($_SERVER['GEOIP_LATITUDE'], $_SERVER['GEOIP_LONGITUDE'])) {
return [
'lat' => floatval($_SERVER['GEOIP_LATITUDE']),
'lng' => floatval($_SERVER['GEOIP_LONGITUDE'])
];
}
// Fallback to country center
if (isset($_SERVER['GEOIP_COUNTRY_CODE'])) {
return $this->get_country_center($_SERVER['GEOIP_COUNTRY_CODE']);
}
// Default to New York
return ['lat' => 40.7128, 'lng' => -74.0060];
}
private function get_nearby_stores($radius = 50) {
return get_posts([
'post_type' => 'store_location',
'posts_per_page' => 10,
'meta_query' => [
[
'key' => 'latitude',
'value' => [
$this->visitor_location['lat'] - ($radius / 111),
$this->visitor_location['lat'] + ($radius / 111)
],
'type' => 'NUMERIC',
'compare' => 'BETWEEN'
]
]
]);
}
…

在这里,您可以使用 $store_locator->render_map() 将地图添加到模板中。

动态内容交付

特定位置的内容、定价和促销是依赖地理定位的基础应用。位置感知内容交付可让您根据访问者的位置定制网站内容、价格和促销活动。

要在您的项目中实现这一点,您需要针对不同地区的不同内容。然后,您就可以开始创建一种可靠的方法来处理位置数据并生成缓存密钥。这样既能确保高效的内容交付,又能保持位置感知:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
private function get_location_context() {
// Create unique cache keys based on location data
$context = [
'country' => $_SERVER['GEOIP_COUNTRY_CODE'] ?? null,
'region' => $_SERVER['GEOIP_REGION'] ?? null,
'locale' => get_locale()
];
// Add currency and timezone data if needed
if ($this->requires_currency_handling) {
$context['currency'] = $this->get_country_currency($context['country']);
}
return $context;
}
private function get_location_context() { // Create unique cache keys based on location data $context = [ 'country' => $_SERVER['GEOIP_COUNTRY_CODE'] ?? null, 'region' => $_SERVER['GEOIP_REGION'] ?? null, 'locale' => get_locale() ]; // Add currency and timezone data if needed if ($this->requires_currency_handling) { $context['currency'] = $this->get_country_currency($context['country']); } return $context; }
private function get_location_context() {
// Create unique cache keys based on location data
$context = [
'country' => $_SERVER['GEOIP_COUNTRY_CODE'] ?? null,
'region'  => $_SERVER['GEOIP_REGION'] ?? null,
'locale'  => get_locale()
];
// Add currency and timezone data if needed
if ($this->requires_currency_handling) {
$context['currency'] = $this->get_country_currency($context['country']);
}
return $context;
}

这为任何基于地理位置的决策和内容处理系统奠定了基础。这既可以处理定价问题,也可以处理地区内容差异问题:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
private function process_dynamic_content($content, $context) {
// Handle pricing with currency conversion
if (strpos($content, '{price:') !== false) {
$content = preg_replace_callback(
'/\{price:([0-9]+\.?[0-9]*)\}/',
fn($matches) => $this->format_price(
floatval($matches[1]),
$context['currency']
),
$content
);
}
// Process regional content blocks
if (strpos($content, '[region:') !== false) {
$content = preg_replace_callback(
'/\[region:([^\]]+)\](.*?)\[\/region\]/s',
function($matches) use ($context) {
$regions = array_map('trim', explode(',', $matches[1]));
return in_array($context['country'], $regions) ? $matches[2] : '';
},
$content
);
}
return $content;
}
private function process_dynamic_content($content, $context) { // Handle pricing with currency conversion if (strpos($content, '{price:') !== false) { $content = preg_replace_callback( '/\{price:([0-9]+\.?[0-9]*)\}/', fn($matches) => $this->format_price( floatval($matches[1]), $context['currency'] ), $content ); } // Process regional content blocks if (strpos($content, '[region:') !== false) { $content = preg_replace_callback( '/\[region:([^\]]+)\](.*?)\[\/region\]/s', function($matches) use ($context) { $regions = array_map('trim', explode(',', $matches[1])); return in_array($context['country'], $regions) ? $matches[2] : ''; }, $content ); } return $content; }
private function process_dynamic_content($content, $context) {
// Handle pricing with currency conversion
if (strpos($content, '{price:') !== false) {
$content = preg_replace_callback(
'/\{price:([0-9]+\.?[0-9]*)\}/',
fn($matches) => $this->format_price(
floatval($matches[1]), 
$context['currency']
),
$content
);
}
// Process regional content blocks
if (strpos($content, '[region:') !== false) {
$content = preg_replace_callback(
'/\[region:([^\]]+)\](.*?)\[\/region\]/s',
function($matches) use ($context) {
$regions = array_map('trim', explode(',', $matches[1]));
return in_array($context['country'], $regions) ? $matches[2] : '';
},
$content
);
}
return $content;
}

这样,您就可以在内容中使用简单的标记,自动适应访客的位置。例如

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
[region:US,CA]
<p>Free shipping on orders over {price:50}!</p>
[/region]
[region:GB,DE,FR]
<p>Free shipping on orders over {price:45}!</p>
[/region]
[region:US,CA] <p>Free shipping on orders over {price:50}!</p> [/region] [region:GB,DE,FR] <p>Free shipping on orders over {price:45}!</p> [/region]
[region:US,CA]
<p>Free shipping on orders over {price:50}!</p>
[/region]
[region:GB,DE,FR]
<p>Free shipping on orders over {price:45}!</p>
[/region]

有了简单的实施,您就可以对其进行性能优化。这一步可能会被您忽略,但高效的缓存可以帮助您保持位置感知内容的性能。Kinsta 的缓存模型非常适合这项工作。

表单预填充和验证

创建位置感知表单需要处理不同地区的地址格式、邮政编码和电话号码。因此,为这些不同地区定义验证和格式化规则非常重要:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
private $format_patterns = [
'US' => [
'postal' => [
'pattern' => '^(?=.{5,10}$)\d{5}(-\d{4})?$',
'transform' => fn($value) => strtoupper(trim($value))
],
'phone' => [
'pattern' => '^\+1[2-9]\d{2}[2-9]\d{2}\d{4}$',
'transform' => fn($value) => '+1' . preg_replace('/\D/', '', $value)
]
],
'GB' => [
'postal' => [
'pattern' => '^(?=.{6,8}$)[A-Z]{1,2}[0-9][A-Z0-9]? ?[0-9][A-Z]{2}$',
'transform' => fn($value) => preg_replace(
'/^(.+?)([0-9][A-Z]{2})$/',
'$1 $2',
strtoupper(trim($value))
)
]
]
];
private $format_patterns = [ 'US' => [ 'postal' => [ 'pattern' => '^(?=.{5,10}$)\d{5}(-\d{4})?$', 'transform' => fn($value) => strtoupper(trim($value)) ], 'phone' => [ 'pattern' => '^\+1[2-9]\d{2}[2-9]\d{2}\d{4}$', 'transform' => fn($value) => '+1' . preg_replace('/\D/', '', $value) ] ], 'GB' => [ 'postal' => [ 'pattern' => '^(?=.{6,8}$)[A-Z]{1,2}[0-9][A-Z0-9]? ?[0-9][A-Z]{2}$', 'transform' => fn($value) => preg_replace( '/^(.+?)([0-9][A-Z]{2})$/', '$1 $2', strtoupper(trim($value)) ) ] ] ];
private $format_patterns = [
'US' => [
'postal' => [
'pattern' => '^(?=.{5,10}$)\d{5}(-\d{4})?$',
'transform' => fn($value) => strtoupper(trim($value))
],
'phone' => [
'pattern' => '^\+1[2-9]\d{2}[2-9]\d{2}\d{4}$',
'transform' => fn($value) => '+1' . preg_replace('/\D/', '', $value)
]
],
'GB' => [
'postal' => [
'pattern' => '^(?=.{6,8}$)[A-Z]{1,2}[0-9][A-Z0-9]? ?[0-9][A-Z]{2}$',
'transform' => fn($value) => preg_replace(
'/^(.+?)([0-9][A-Z]{2})$/', 
'$1 $2', 
strtoupper(trim($value))
)
]
]
];

这些模式可处理不同国家邮政编码和电话号码的常见差异。但是,不同国家的地址格式要求各不相同。您可以使用系统化的方法来处理这些差异:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
private function get_address_format($country_code) {
$formats = [
'US' => [
'required' => ['street', 'city', 'state', 'postal'],
'order' => ['street', 'street2', 'city', 'state', 'postal'],
'state_label' => 'State',
'state_type' => 'select'
],
'GB' => [
'required' => ['street', 'city', 'postal'],
'order' => ['street', 'street2', 'city', 'county', 'postal'],
'state_label' => 'County',
'state_type' => 'text'
]
];
return $formats[$country_code] ?? $formats['US'];
}
private function get_address_format($country_code) { $formats = [ 'US' => [ 'required' => ['street', 'city', 'state', 'postal'], 'order' => ['street', 'street2', 'city', 'state', 'postal'], 'state_label' => 'State', 'state_type' => 'select' ], 'GB' => [ 'required' => ['street', 'city', 'postal'], 'order' => ['street', 'street2', 'city', 'county', 'postal'], 'state_label' => 'County', 'state_type' => 'text' ] ]; return $formats[$country_code] ?? $formats['US']; }
private function get_address_format($country_code) {
$formats = [
'US' => [
'required' => ['street', 'city', 'state', 'postal'],
'order' => ['street', 'street2', 'city', 'state', 'postal'],
'state_label' => 'State',
'state_type' => 'select'
],
'GB' => [
'required' => ['street', 'city', 'postal'],
'order' => ['street', 'street2', 'city', 'county', 'postal'],
'state_label' => 'County',
'state_type' => 'text'
]
];
return $formats[$country_code] ?? $formats['US'];
}

接下来是字段验证。您应该实施符合地区格式要求的验证:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
private function validate_field($field, $value, $country_code) {
if (!isset($this->format_patterns[$country_code][$field])) {
return true; // No specific validation needed
}
$pattern = $this->format_patterns[$country_code][$field]['pattern'];
$transform = $this->format_patterns[$country_code][$field]['transform'];
// Transform value before validation
$value = $transform($value);
return (bool)preg_match("/$pattern/", $value);
}
private function validate_field($field, $value, $country_code) { if (!isset($this->format_patterns[$country_code][$field])) { return true; // No specific validation needed } $pattern = $this->format_patterns[$country_code][$field]['pattern']; $transform = $this->format_patterns[$country_code][$field]['transform']; // Transform value before validation $value = $transform($value); return (bool)preg_match("/$pattern/", $value); }
private function validate_field($field, $value, $country_code) {
if (!isset($this->format_patterns[$country_code][$field])) {
return true;  // No specific validation needed
}
$pattern = $this->format_patterns[$country_code][$field]['pattern'];
$transform = $this->format_patterns[$country_code][$field]['transform'];
// Transform value before validation
$value = $transform($value);
return (bool)preg_match("/$pattern/", $value);
}

一个小片段将这一切串联起来:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Validate a postal code
if (!$this->validate_field('postal', $_POST['postal_code'], 'GB')) {
$errors[] = 'Invalid postal code format';
}
// Validate a postal code if (!$this->validate_field('postal', $_POST['postal_code'], 'GB')) { $errors[] = 'Invalid postal code format'; }
// Validate a postal code
if (!$this->validate_field('postal', $_POST['postal_code'], 'GB')) {
$errors[] = 'Invalid postal code format';
}

这种实施将自动适应访问者的位置,处理地址格式的地区差异,为特定位置字段提供适当的验证,并在不同地区保持数据的完整性。

小结

实施 WordPress 地理定位可让您创建强大的位置感知应用程序。启用 IP 地理定位工具意味着您可以利用服务器级的强大、高性能和用户友好的功能。更重要的是,它以隐私为中心,并且合规。

您对 WordPress 地理定位和项目实施有任何疑问吗?请在下面的评论区告诉我们!

评论留言