針對訪客進行地理位置定位簡易版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 地理定位和專案實施有任何疑問嗎?請在下面的評論區告訴我們!

評論留言