Woocommerce 本身提供的商品網址格式預設情況下只能用 post_id
、post_name
,但在非英文語系的情況下,固定網址往往會變成一長串,因為中文會先經過 URL encoding。
假設商品名稱是 推車置物架 置物推車
,
那網址就會變成 your-domain.com/product/%E6%8E%A8%E8%BB%8A%E7%BD%AE%E7%89%A9%E6%9E%B6-%E7%BD%AE%E7%89%A9%E6%8E%A8%E8%BB%8A,這個在轉發給他人時既無法閱讀也占版面。
但假如說換成 post_id,又能讓人從 post_id 來推測出賣場訂單數量,假設我 7 月份時上架一支商品,當時 post_id 是1000,結果8月份時再上架一支新商品 post_id 卻只到 1200。這樣就可以合理推斷過去一個月的時間產生的頁面、圖片、訂單、商品數量才不到200,可見賣場訂單量之少。
在商業上讓人摸清底牌的話對價格的談判可不有利,所以理想的網址應該要把賣場中會出現 post_id 的項目都給隱藏,並且自動產生足夠段並帶有一定識別度的網址。
Woocommerce 有提供2個 action:
- woocommerce_product_import_inserted_product_object:批次匯入時觸發 (包括匯入更新也算)
- woocommerce_new_product:新增商品時觸發
透過這兩個 action,就可以在商品上架的那一刻一併寫入自定義的固定網址 (sulg)
add_action('woocommerce_new_product', function ($product_id) {
n263_generate_product_hash_url(wc_get_product($product_id));
});
add_action('woocommerce_product_import_inserted_product_object', function ($product, $data) {
n263_generate_product_hash_url($product);
}, 10, 2);
// 透過商品ID產生自訂URL
function n263_generate_product_hash_url($product)
{
// 確保是主商品不是變化類型
if (!$product || $product->is_type('variation'))
return;
// 先檢查是否已經有格式為 yymm-隨機8碼 的固定網址,若有則跳過
$id = $product->get_id();
$current_slug = get_post_field('post_name', $id);
if ($current_slug && preg_match('/^\d{4}-[a-z0-9]{8}$/', $current_slug)) {
return;
}
// 產生自訂唯一網址,並檢查是否重複,若重複則最多重新產生5次
for ($i = 0; $i < 5; $i++) {
$random = substr(base_convert(bin2hex('1' . random_bytes(8)), 16, 36), 1, 8);
$slug = date("ym") . '-' . $random;
$exists = get_page_by_path($slug, OBJECT, 'product'); //檢查URL是否已經存在
if (!$exists)
break;
}
// 更新 Slug
wp_update_post([
'ID' => $id,
'post_name' => $slug //將商品網址設定為 yymm-隨機8碼 的格式
]);
}
這樣當商品新增、匯入時就會自動執行這一段短代碼,只要不是變化類型的商品 且 商品的固定連結不是 yymm-8碼隨機的格式,就會幫他產生一組固定網址,避免使用 post_id ,又不會有 postname 超長一串的網址。
不過要注意的是,即使網址中隱藏了post_id,在商品頁面的 HTML > body 中的 class 還是會出現 postid,這部份要用另一段短代碼隱藏。

add_filter( 'body_class', 'n263_body_class_remove_postid' );
function n263_body_class_remove_postid( $classes ) {
// 過濾掉以 postid- 開頭的 class
return array_filter(
$classes,
function( $class ) {
return strpos( $class, 'postid-' ) !== 0 ;
}
);
}
body_class 這個 filter 會把所有的 Class 透過 $classes 這個陣列傳進來,這時我們只需要用 array_filter 過濾掉文字中包含 postid 的 class 即可。