WordPress 自動將文章 slug 設為文章 ID 的實作技巧

前言

在 WordPress 中,文章的 slug(網址別名)預設是根據標題自動產生,或由使用者手動設定。有時候為了簡化網址管理或避免重複 slug,會想將 slug 固定為文章的 ID。這段程式碼示範如何在文章發佈或排程時,自動將 slug 設為文章 ID,適合需要統一網址格式的開發者或網站管理者。

為什麼要用文章 ID 當 slug?

文章 ID 是 WordPress 中每篇文章的唯一識別碼,使用 ID 當 slug 可以避免重複 slug 的問題,也方便在程式中直接以 ID 取用文章,提升網址的穩定性與一致性。

程式碼解析

1. 使用 save_post Hook

程式碼利用 WordPress 的 save_post action,這個鉤子會在文章儲存時觸發,能即時修改文章資料。

add_action('save_post', function ($post_id, $post, $update) {
    // 程式內容
}, 20, 3);

這裡設定優先權為 20,並且接受三個參數:文章 ID、文章物件、是否為更新。

2. 基本保護條件

避免對修訂版本(revision)或自動儲存(autosave)執行,減少不必要的處理。

if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) return;
if (!is_object($post)) return;

3. 限制只對文章類型生效

確保只對 post 文章類型操作,不影響其他自訂文章類型。

if ($post->post_type !== 'post') return;

4. 限制文章狀態

只對「已發佈(publish)」與「排程(future)」的文章改寫 slug,避免草稿或其他狀態執行。

$allowed_status = ['publish', 'future'];
if (!in_array($post->post_status, $allowed_status, true)) return;

5. 判斷是否已是目標 slug

如果 slug 已經是文章 ID,則不再重寫,避免重複操作。

$target_slug = (string) $post_id;
if ($post->post_name === $target_slug) return;

6. 防止無限迴圈

wp_update_post 會再次觸發 save_post,容易造成無限迴圈。使用靜態變數 $running 作為旗標,避免重複執行。

static $running = false;
if ($running) return;
$running = true;

7. 更新 slug

呼叫 wp_update_post 更新文章的 post_name 欄位為文章 ID。

wp_update_post([
    'ID'        => $post_id,
    'post_name' => $target_slug,
]);
$running = false;

實務應用與延伸

  • 適合網站需要簡潔、唯一的網址結構,或是避免標題相同導致 slug 重複的情況。
  • 可擴充支援更多文章類型或狀態,只要調整條件判斷即可。
  • 若需保留原本 slug,可改成在自訂欄位或其他欄位存 ID。
  • 注意排程文章的 slug 會在排程時間前就設定完成。

常見問題與注意事項

  • 無限迴圈問題是此類操作常見的陷阱,務必使用旗標避免。
  • 使用文章 ID 當 slug 可能不利 SEO,需評估是否符合網站策略。
  • 若網站有使用快取或重寫規則,更新 slug 後可能需要清除快取。

完整程式碼

<?php
/**
 * Post 發佈時固定把 slug 設為文章 ID
 * - 只針對 post(文章)
 * - 避免無限迴圈
 * - 若已是 ID slug 就不重寫
 * - 支援 publish / future(排程)/ 更新時維持
 */

add_action('save_post', function ($post_id, $post, $update) {

    // 1) 基本保護
    if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) return;
    if (!is_object($post)) return;

    // 2) 只針對文章 post
    if ($post->post_type !== 'post') return;

    // 3) 只在這些狀態下固定(你也可以只留 publish)
    $allowed_status = ['publish', 'future']; // future = 排程
    if (!in_array($post->post_status, $allowed_status, true)) return;

    // 4) 目標 slug = 文章 ID
    $target_slug = (string) $post_id;

    // 5) 已經是目標 slug 就不做事
    if ($post->post_name === $target_slug) return;

    // 6) 防止 save_post -> wp_update_post -> save_post 無限迴圈
    static $running = false;
    if ($running) return;
    $running = true;

    // 7) 更新 slug
    wp_update_post([
        'ID'        => $post_id,
        'post_name' => $target_slug,
    ]);

    $running = false;

}, 20, 3);