跳到主要內容
龐果設計 龐果設計
  • 品牌設計
    • VIS設計
    • Logo設計
    • 名片設計
    • 型錄設計
    • 包裝設計
  • 網頁設計
    • 一頁式網頁
    • 形象網頁設計
    • 電商網頁設計
  • WordPress 服務
    • WordPress 故障排除
    • WordPress 網站搬家
  • 設計案例
  • 關於我們
  • 觀點聚焦
  • 品牌設計
    • VIS設計
    • Logo設計
    • 名片設計
    • 型錄設計
    • 包裝設計
  • 網頁設計
    • 一頁式網頁
    • 形象網頁設計
    • 電商網頁設計
  • WordPress 服務
    • WordPress 故障排除
    • WordPress 網站搬家
  • 設計案例
  • 關於我們
  • 觀點聚焦
首頁 » 觀點聚焦 » meta box自訂欄位怎麼做?免裝外掛,4 步驟寫進 wp_postmeta
Wordpress

meta box自訂欄位怎麼做?免裝外掛,4 步驟寫進 wp_postmeta

發布時間:2026.09.19 編輯:Haden

多數人一聽到「幫文章加一個 meta box 自訂欄位」,反射動作是先去外掛市集找一套現成方案,裝上去、拖幾個欄位就收工。但如果需求只是一兩個簡單欄位,比如一個文字框存副標題、一個下拉選單選版型,其實不必為此多背一個外掛相依,WordPress 核心本身就內建一整套機制,讓你自己刻出跟內建欄位一樣的介面,資料照樣寫進同一張資料表,也不用等外掛作者更新相容性。

這套機制叫 meta box,處理的正是自訂欄位。它不是另一套資料庫,而是一個你自己寫的介面,包在文章編輯畫面裡,讓資料寫進 WordPress 原本就有的 wp_postmeta 這張表。搞不清楚 meta box、內建的「自訂欄位」面板,還有外掛 ACF 這 3 個名詞怎麼分工,是很多人一開始就搞混的地方,遇到問題時也抓不到方向查起。

先把這 3 層關係拆開來看,就會知道「不裝外掛」實際上跳過了哪一層、又保留了哪一層。

meta box 是什麼?和 ACF、內建自訂欄位面板的分工

WordPress 從很早的版本開始,就替每一篇文章、每一個頁面準備了一張額外的資料表,叫 wp_postmeta。除了標題、內文、發布時間這些主要欄位存在 wp_posts,其餘任何額外的資訊,不管是一個評分、一個副標題,還是一組座標,都可以用鍵值(key/value)的形式塞進 wp_postmeta,一篇文章可以對應無限多筆。操作它的原生函式只有 4 個:add_post_meta() 新增一筆、update_post_meta() 更新(沒有就自動新增)、get_post_meta() 讀出來、delete_post_meta() 刪掉。這篇後面所有的存值與讀值,最終都在呼叫這 4 個函式的其中一個,沒有例外。

WordPress 後台原本就附了一個很陽春的介面來操作這張表,叫「自訂欄位」面板,兩個文字框,一個填鍵名、一個填值,適合臨時加一筆資料。區塊編輯器上線之後,這個面板預設是關閉的,得自己到編輯畫面右上角的選項選單裡找到「偏好設定」,在「面板」分類裡把「自訂欄位」打開才看得到,不少人第一次找不到這個面板,還以為功能被拿掉了。

meta box 不是另一套資料儲存方式,而是開發者自己寫的一段程式碼,用來取代那個陽春的兩欄位介面。與其讓編輯者手動打鍵名、打值,你可以用 meta box 印出一個文字框、一個下拉選單,甚至一組核取方塊,讓欄位有清楚的標籤、預先定義好的選項,資料照樣寫進同一張 wp_postmeta,只是介面漂亮許多,也不容易打錯鍵名。文章編輯畫面裡熟悉的「發布」「分類」「特色圖片」這幾個側欄區塊,本質上也都是 meta box,只是核心團隊自己註冊的,外掛想加自己的區塊,用的是同一套 API。

至於 ACF(Advanced Custom Fields),它做的事情本質上跟自己寫 meta box 一樣,包裝一層更好用的介面,只是別人已經把大量欄位型態(圖片、關聯文章、彈性內容、重複欄位)都寫好了,只要在後台點一點就能新增欄位,不用碰程式碼。它存資料的終點仍然是 wp_postmeta,這代表就算網站用了 ACF,你或別的外掛一樣可以用原生的 get_post_meta() 讀到那筆值,只要知道 meta_key 叫什麼;反過來,用 ACF 提供的 get_field() 讀,內部也只是幫你多做了一層型態轉換,查的還是同一張表。搞懂這一層,就會知道自己刻 meta box 自訂欄位跟裝 ACF 從來不是兩套互斥的資料系統,只是介面複雜度與開發成本的取捨。

內建自訂欄位面板、自己寫 meta box、ACF 三種介面,資料最後都寫進同一張 wp_postmeta 資料表
內建面板、自己刻的 meta box 自訂欄位、ACF 外掛只是三種介面,資料終點都是同一張 wp_postmeta,差別只在開發成本。

只有一兩個欄位時,該不該為此裝一支外掛

決定要不要為了一兩個欄位去裝一支外掛,其實有幾個具體的判斷點,不是「外掛都不好」這種空泛立場。第一個判斷點是欄位數量,如果整篇文章只需要一兩個額外資訊,比如一個文字框存活動地點、一個下拉選單選文章類型,多裝一支專門管理自訂欄位的外掛,換來的複雜度往往比省下的力氣還大。第二個判斷點是欄位型態,純文字、下拉選單、核取方塊這類基本型態,自己刻的成本很低,但如果需要重複欄位(一篇文章可以加無限筆團購資訊)、彈性內容(不同區塊自由排列組合)這種進階型態,自己刻的工程量會迅速膨脹,外掛的價值這時候就出來了。

第三個判斷點是使用者,這個欄位只給自己或內部工程師填,還是要讓完全不懂程式的同事、客戶天天在後台操作。自己刻的介面通常只做到堪用,錯誤處理、欄位說明、排版彈性都比不上成熟外掛,填欄位的人不是你的話,介面好不好用會直接變成客服工作量。第四個判斷點是相依性,已經在維護一個佈景主題或外掛的人,通常會希望依賴的外部套件愈少愈好,每多一個外掛,就多一份要跟著更新、跟著版本相容性走的負擔,自己刻反而更省心。

誠實一點說,選擇自己寫,代價也不小,渲染表單的 HTML 要自己寫、nonce 驗證要自己掛、依欄位型態清理資料的邏輯要自己刻,這些外掛都已經幫忙做好、也經過大量網站驗證過。所以這兩條路不是「原生比較高級、外掛比較懶」的對立關係,而是互補,欄位一多、要交給非技術人員維護、需要重複欄位或彈性內容時,ACF 仍然是對的選擇;欄位少、型態單純、只給開發者自己用,原生寫法才划算。

用 add_meta_box 把欄位框架釘進編輯畫面

要讓一個 meta box 出現在編輯畫面,第一步是呼叫 add_meta_box() 這個函式,但有一個容易忽略的規定,它一定要包在 add_meta_boxes 這個掛勾(hook)裡呼叫,不能直接寫在 admin_init 之類的地方。原因是每次進到文章編輯畫面,WordPress 都要重新問一次這個畫面該顯示哪些 meta box,這件事發生在 add_meta_boxes 這個時間點,若在別的時機點註冊,就等於錯過了 WordPress 詢問的那個瞬間,meta box 自然不會出現。

add_action( 'add_meta_boxes', 'pongo_register_meta_box' );

function pongo_register_meta_box() {
    add_meta_box(
        'pongo_extra_info',           // id
        '額外資訊',                    // title
        'pongo_render_meta_box',      // callback
        [ 'post', 'page' ],           // screen
        'side',                       // context
        'high'                        // priority
    );
}Code language: PHP (php)

add_meta_box() 拿到的第一個參數是 id,這是這個 meta box 的唯一識別碼,會出現在 HTML 的 id 屬性上;建議自己取一個有前綴的名字,例如 pongo_extra_info 而不是單純的 extra_info,避免跟別的外掛或佈景主題剛好取到同一個名字互相覆蓋。第二個參數 title 就是顯示在區塊標題列上的文字,編輯者看到的就是這個字串。第三個參數 callback 是實際負責印出欄位內容的函式。

第四個參數 screen 決定這個 meta box 要出現在哪些畫面,可以傳一個字串(只給文章用就傳 ‘post’),也可以傳一個陣列同時註冊到多種文章類型(如上面範例的 [‘post’, ‘page’]),甚至能傳一個 WP_Screen 物件做更精細的判斷。這裡有個常被誤會的地方,不需要另外處理「儲存按鈕」,meta box 裡的表單欄位本來就跟文章標題、內文放在同一個 HTML 表單裡,編輯者按下發布或更新時,你的欄位資料會跟著整份表單一起送出,不用自己刻一個獨立的儲存按鈕或 AJAX 請求。

決定欄位位置的是 context 與 priority,不是 screen

決定欄位出現在畫面哪裡的其實是 context 這個第五個參數,而不是 screen。它有 3 個可用值,’normal’ 讓 meta box 出現在內容編輯區下方,版面比較寬,適合欄位數量多、需要完整表單版面的情況;’side’ 讓 meta box 出現在右側欄,跟「發布」「分類」擠在同一條窄欄裡,適合一兩個簡單欄位;’advanced’ 則是更下面的位置,也是沒特別指定時的預設值,實務上很少有編輯者會特地捲到那麼下面去看。

同一個 context 裡如果有好幾個 meta box 排在一起,誰先誰後就看第六個參數 priority,可用值是 ‘high’、’core’、’default’、’low’ 這 4 種,數值愈靠前的排愈上面。實務上的判斷很單純,只有一兩個欄位、希望編輯者一打開文章就馬上看到,就用 side 搭配 high;欄位數量多、需要比較寬的版面把欄位排開,就用 normal,priority 留預設的 default 就好,不用刻意去搶排序。

add_meta_box 的 context 參數決定 meta box 出現在編輯畫面的 normal、side 或 advanced 位置
context 設 normal 會出現在內容編輯區下方、side 擠進右側欄、advanced 則落在更下面的預設位置。

在 callback 函式裡畫出文字框與下拉選單

callback 函式的第一個、也是唯一固定會拿到的參數,就是目前這篇文章的 $post 物件。這個函式要做的事情很直接,用 echo 把 HTML 表單元素印出來,讓它出現在剛剛註冊的那個區塊裡。第一步通常是用 get_post_meta( $post->ID, $key, true ) 把已經存在的值撈出來,第三個參數傳 true 代表只要單一值而不是陣列,這樣編輯者重新打開文章時,才能看到自己上次填的內容,而不是每次都是空白表單。

function pongo_render_meta_box( $post ) {
    $subtitle = get_post_meta( $post->ID, '_pongo_subtitle', true );

    wp_nonce_field( 'pongo_save_meta_box', 'pongo_meta_box_nonce' );

    echo '<p><label for="pongo_subtitle">副標題</label></p>';
    echo '<input type="text" id="pongo_subtitle" name="pongo_subtitle" value="' . esc_attr( $subtitle ) . '" style="width:100%;" />';
}Code language: PHP (php)

有一個安全細節新手很容易漏掉,既有的值印回表單時,一定要包上 esc_attr() 這種逃逸函式再輸出到 HTML 屬性裡。資料庫裡存的內容本身可能帶有引號或角括號,沒逃逸的話,這些符號會直接把 value 屬性截斷,版面跑掉是最輕的後果,帶有惡意腳本標籤時甚至可能造成跨站腳本攻擊(XSS)。這個 callback 函式也是印 wp_nonce_field() 的地方,它會在表單裡塞一個隱藏欄位,之後掛在 save_post 那個環節要靠這個值來驗證這次送出的表單真的是從這個編輯畫面本身送出的,不是外部偽造的請求。一個 meta box 建議用一個獨立的 nonce action 名稱,不要跟別的 meta box 共用同一組,避免驗證邏輯互相干擾。

文字欄位和多行文字框的取值與輸出

單行文字用 input type=”text” 最直觀,值放在 value 屬性裡,一樣要包 esc_attr()。多行文字則用 textarea,差別在於它的值不是放在屬性裡,而是印在開始標籤與結束標籤之間,這裡有個容易忽略的細節,標籤和內容之間不能留任何空白或換行,不然存進去的值前後會莫名其妙多出空格,之後顯示在前台時看起來就是排版跑掉。

$notes = get_post_meta( $post->ID, '_pongo_notes', true );
echo '<textarea id="pongo_notes" name="pongo_notes" rows="4" style="width:100%;">' . esc_textarea( $notes ) . '</textarea>';Code language: PHP (php)

兩種欄位都要先用 get_post_meta() 撈值,撈到的值不存在時,最常見於文章第一次新增、meta 值還沒被建立過,要給一個安全的預設,通常就是空字串。get_post_meta() 在單一值模式下,找不到資料本來就會回傳空字串,只要沒有自己額外處理成 null 或做多餘的判斷,通常就不會跳出 PHP 的警告或提示訊息;但如果另外包了一層邏輯去處理這個回傳值,記得順手補上空字串預設,別讓它變成未定義變數的來源。

核取方塊、下拉選單的勾選狀態與儲存邏輯

核取方塊要不要印出 checked 屬性,用 WordPress 內建的 checked() 這個輔助函式來判斷最保險,它會拿目前存的值跟你指定要比對的值做比較,相符就自動印出 checked=”checked”,比自己寫一個三元運算式更不容易出錯。下拉選單同理,用 selected() 判斷哪個 option 該被標記為目前選中的項目,用法完全對稱。

$featured = get_post_meta( $post->ID, '_pongo_featured', true );
echo '<label><input type="checkbox" name="pongo_featured" value="1" ' . checked( $featured, '1', false ) . ' /> 設為精選</label>';

$layout = get_post_meta( $post->ID, '_pongo_layout', true );
echo '<select name="pongo_layout">';
echo '<option value="default" ' . selected( $layout, 'default', false ) . '>預設版型</option>';
echo '<option value="wide" ' . selected( $layout, 'wide', false ) . '>寬版型</option>';
echo '</select>';Code language: PHP (php)

這裡有個一定要記住的規則,核取方塊如果沒被勾選,表單送出時 $_POST 裡根本不會出現這個欄位的鍵名,不是傳一個空字串或 0 過來,是完全不存在。這代表儲存的那段程式碼不能只寫「有送出這個值就存」,還得處理「這個欄位完全沒出現在 $_POST 裡」這種情況,才能正確判斷成使用者把核取方塊取消勾選,而不是誤以為欄位沒被改動、繼續沿用舊值。

掛上 save_post,把資料安全地存回 postmeta

存資料的邏輯全部寫在掛勾在 save_post 這個動作(action hook)的函式裡,這個 hook 會在文章被建立或更新的當下觸發。第一步永遠是驗證 nonce,用 wp_verify_nonce() 去比對表單裡那個隱藏欄位,確認這次的儲存請求真的是從剛剛印出來的那個編輯畫面送出的,不是有人拼湊出一個看起來像的 POST 請求就直接打過來。驗證失敗,函式要立刻 return,後面所有動作都不執行。

add_action( 'save_post', 'pongo_save_meta_box' );

function pongo_save_meta_box( $post_id ) {

    // 1. 驗證 nonce
    if ( ! isset( $_POST['pongo_meta_box_nonce'] ) ||
         ! wp_verify_nonce( $_POST['pongo_meta_box_nonce'], 'pongo_save_meta_box' ) ) {
        return;
    }

    // 2. 排除自動儲存
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }

    // 3. 檢查使用者權限
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    // 4. 清理並寫入資料
    if ( isset( $_POST['pongo_subtitle'] ) ) {
        $subtitle = sanitize_text_field( $_POST['pongo_subtitle'] );
        if ( '' !== $subtitle ) {
            update_post_meta( $post_id, '_pongo_subtitle', $subtitle );
        } else {
            delete_post_meta( $post_id, '_pongo_subtitle' );
        }
    }

    if ( isset( $_POST['pongo_notes'] ) ) {
        update_post_meta( $post_id, '_pongo_notes', sanitize_textarea_field( $_POST['pongo_notes'] ) );
    }

    if ( isset( $_POST['pongo_featured'] ) ) {
        update_post_meta( $post_id, '_pongo_featured', '1' );
    } else {
        delete_post_meta( $post_id, '_pongo_featured' );
    }
}Code language: PHP (php)

第二步排除自動儲存。區塊編輯器每隔一段時間會在背景觸發一次自動儲存,由常數 DOING_AUTOSAVE 標記,這個當下表單其實沒有真的被編輯者按下送出,欄位邏輯如果沒排除這個情況,欄位值就有可能被自動儲存帶來的舊資料覆蓋掉,讓編輯者感覺明明改了、怎麼又跳回去。

第三步是用 current_user_can( ‘edit_post’, $post_id ) 檢查目前這位使用者真的有權限編輯這篇文章。少了這一步,理論上任何能組出一個 POST 請求的人,不只是被授權的編輯者,都可能靠偽造請求寫入資料,這一步是擋掉這種情況的最後一道防線。

第四步依欄位型態挑對應的清理函式,不能全部都用同一個。單行文字用 sanitize_text_field(),它會去掉標籤、清掉多餘的換行與空白;多行文字要改用 sanitize_textarea_field(),兩者的差別在於後者會保留換行與段落間的空白,拿 sanitize_text_field() 去處理一段多行的備註,換行全部會被吃掉,變成擠在一起的一整段。網址類的欄位則用 sanitize_url(),這是 WordPress 5.9 起恢復使用的正式函式名稱,舊教學常寫的 esc_url_raw() 目前仍然可以用,但屬於比較舊的寫法。

第五步,值有內容就用 update_post_meta() 寫入;欄位被清空,不管是文字被使用者刪光,還是核取方塊從勾選變成取消勾選、$_POST 裡完全沒帶這個鍵,都要用 delete_post_meta() 把它清掉,不要讓資料表留著一堆空字串。這裡補兩個進階細節,如果這個 meta box 只服務單一文章類型,掛 save_post_{$post_type},例如 save_post_product,會比掛通用的 save_post 更精準,觸發次數也更少,因為它只會在那個特定文章類型儲存時才跑;另外若這段儲存邏輯裡呼叫了 wp_update_post(),要特別注意這個函式本身會再次觸發 save_post,形成無窮迴圈,正確做法是在呼叫前先用 remove_action() 把自己這個掛勾拿掉,呼叫完再用 add_action() 加回來。

save_post 存值前要先過 nonce、自動儲存、權限、清理四道關卡,通過才寫進 wp_postmeta
存值前先驗 nonce、排除自動儲存、檢查權限、依型態清理,四關都通過才用 update_post_meta 寫進 wp_postmeta。

底線開頭的欄位在後台預設隱藏,並非沒有寫入資料庫

把前面的步驟都做完、發布文章之後,跑去後台內建的「自訂欄位」面板想確認資料真的存進去了,卻發現面板裡完全看不到剛剛存的東西,這是新手最容易誤解、卻很少被教學明確講清楚的一個困惑,常常會誤以為是儲存失敗。真正的原因並不是 bug,是 WordPress 明文規定的行為,meta_key 只要以底線開頭,範例裡的 _pongo_subtitle 正是這種寫法,就不會出現在內建自訂欄位面板的清單裡,也不會被 the_meta() 這個模板函式列出來。

這其實是刻意的設計,用意是把只給自己 meta box 使用的資料,跟開放給一般使用者透過陽春面板手動輸入的資料分開,避免使用者在那個什麼提示都沒有的兩欄位介面裡,不小心把精心設計的欄位改壞或整筆刪掉。想親眼確認資料有沒有真的寫進資料庫,有 2 個實際可行的驗證方法,一是暫時把 meta_key 改成不加底線的名字,重新整理編輯畫面看它會不會出現在自訂欄位面板裡,驗證完記得改回底線開頭,否則等於把欄位重新暴露給陽春面板;二是直接在前台的範本檔案裡,把 get_post_meta() 的回傳值印出來,肉眼確認值真的存在。

還有一個容易被忽略的附帶規則,就算 meta_key 沒有加底線前綴,只要這個 meta_value 存的是一個陣列,例如一組核取方塊群組直接存成陣列,一樣不會顯示在文章編輯畫面裡。內建的自訂欄位面板本來就只認得簡單的字串值,陣列型態的資料它處理不了,索性就不顯示。

meta box 在區塊編輯器裡顯示在可收合的分割畫面

區塊編輯器上線之後,用 add_meta_box() 寫的傳統 meta box 不需要另外補一段相容程式碼就能顯示,只要照前面幾節的方式註冊,區塊編輯器預設就會自動把它顯示出來,位置在編輯內容的下方,不需要任何額外設定。

顯示的樣子在 WordPress 6.7(2024 年 11 月發布)之後有明顯變化。從這個版本起,meta box 的區塊改成分割畫面設計,整塊變成可以收合、也可以拖動調整高度的獨立區塊,預設最高只會佔到編輯器畫面的一半左右,避免一個 meta box 太長就把整個編輯畫面吃掉;編輯內容本身則改用 iframe 載入,讓文章內容區塊套用的樣式跟前台實際顯示的樣子更一致,算是所見即所得體驗的延伸。

如果自己寫的 meta box 用到跟區塊編輯器互相衝突的舊技術,最常見的是依賴 TinyMCE 舊版編輯器 API 的欄位,可以明確把 __block_editor_compatible_meta_box 這個旗標設為 false,預設值是 true。設成 false 之後,WordPress 不會硬把一個可能會顯示錯誤或整個壞掉的介面塞進區塊編輯器,而是改顯示一段提示訊息,告訴使用者這個欄位需要切回傳統編輯器才能正常使用。

開放給 REST API 讀寫,需要 register_post_meta 額外註冊

這筆資料能在後台編輯畫面存取,跟這筆資料能不能被 REST API 讀寫,是兩件完全獨立的事,很多人以為只要用 add_meta_box() 把資料存進 wp_postmeta,REST API 回應裡自動就會帶出這筆值,其實不會。要讓一個 meta 欄位出現在 REST API 的回應裡,得另外呼叫 register_post_meta(),並把裡面的 show_in_rest 參數設為 true。

add_action( 'init', 'pongo_register_rest_meta' );

function pongo_register_rest_meta() {
    register_post_meta( 'post', '_pongo_subtitle', [
        'type'              => 'string',
        'single'            => true,
        'show_in_rest'      => true,
        'sanitize_callback' => 'sanitize_text_field',
        'auth_callback'     => function() {
            return current_user_can( 'edit_posts' );
        },
    ] );
}Code language: PHP (php)

register_post_meta() 建議同時帶上 2 個參數,sanitize_callback 負責清理透過 API 送進來的輸入,跟前面存值時的邏輯是同一套原則;auth_callback 則決定誰有權限透過 REST API 讀寫這筆資料,通常檢查 current_user_can( ‘edit_posts’ ) 這類能力就足夠。少了 auth_callback,預設的權限判斷可能比預期的寬鬆或嚴格,直接明講清楚比較不容易出錯。另外有一個容易漏掉的前置條件,這個文章類型本身要支援 custom-fields 這個 supports 項目,register_post_meta() 才會真正生效,若是自訂文章類型,記得在註冊時一併加上這個 supports。

把資料開放給 REST API 讀寫之後,能帶來的實際好處是讓這筆值可以被前端的 headless 應用讀取,或是被區塊編輯器裡用 JavaScript 寫的側欄元件直接讀寫,不用再繞回後台那個傳統的 meta box 介面。側欄元件本身要怎麼用 JavaScript 開發,份量完全不同、是另一個獨立的主題;這裡只需要記得,想接 REST API 或想讓區塊編輯器的側欄直接控制這個欄位,register_post_meta() 這一步不能省。

用類別包裝欄位邏輯,避免函式名稱互相衝突

前面幾節示範的都是全域函式寫法,寫一兩個欄位沒問題,但佈景主題或外掛養大之後,函式名稱在全站範圍很容易撞名,尤其是 pongo_render_meta_box() 這種名字,換一個外掛剛好也取了類似的名字,就會直接觸發 PHP 的函式重複宣告錯誤,網站直接掛掉。

abstract class Pongo_Meta_Box {

    public static function add() {
        add_meta_box(
            'pongo_extra_info',
            '額外資訊',
            [ self::class, 'html' ],
            [ 'post', 'page' ],
            'side',
            'high'
        );
    }

    public static function html( $post ) {
        $subtitle = get_post_meta( $post->ID, '_pongo_subtitle', true );
        wp_nonce_field( 'pongo_save_meta_box', 'pongo_meta_box_nonce' );
        echo '<input type="text" name="pongo_subtitle" value="' . esc_attr( $subtitle ) . '" style="width:100%;" />';
    }

    public static function save( $post_id ) {
        if ( ! isset( $_POST['pongo_meta_box_nonce'] ) ||
             ! wp_verify_nonce( $_POST['pongo_meta_box_nonce'], 'pongo_save_meta_box' ) ) {
            return;
        }
        if ( ! current_user_can( 'edit_post', $post_id ) ) {
            return;
        }
        if ( isset( $_POST['pongo_subtitle'] ) ) {
            update_post_meta( $post_id, '_pongo_subtitle', sanitize_text_field( $_POST['pongo_subtitle'] ) );
        }
    }
}

add_action( 'add_meta_boxes', [ 'Pongo_Meta_Box', 'add' ] );
add_action( 'save_post', [ 'Pongo_Meta_Box', 'save' ] );Code language: PHP (php)

把註冊、渲染、儲存這 3 個步驟包進一個抽象類別的靜態方法裡,掛勾點的地方改用 [ 類別名, ‘方法名’ ] 這種陣列語法取代直接寫函式名稱字串,PHP 就會去呼叫這個類別底下的方法,不用再擔心跟別的外掛或佈景主題撞名。這不是換一種寫法把前面的邏輯重講一次,而是把已經寫好的三段邏輯重新組織進一個結構更耐維護的容器裡,換佈景或外掛時複製這個類別不會撞名,之後如果有第二、第三個欄位群組要用同一套模式,也能靠繼承或把欄位定義抽成類別屬性做參數化,不用整段複製貼上再改名字。

把這整條路線走完一次就會發現,內建的自訂欄位面板、自己刻的 meta box 自訂欄位、外掛提供的 ACF,其實都只是包在同一張 wp_postmeta 資料表外面的不同介面,差別只在誰幫你把表單畫好、把安全機制寫好。要往哪一層動手,看的是欄位的數量與複雜度,不是哪一種比較高級,欄位少、型態單純、只給自己用,自己刻一段 meta box 反而更輕巧;欄位一多、要讓不懂程式的人天天維護,外掛省下的力氣就值回票價。

這兩條路也從來不是二選一,同一個網站,可以用自己刻的 meta box 處理一兩個內部欄位,同時裝一支外掛管理另一批給編輯同事用的複雜欄位,兩邊互不干擾,資料照樣各自安分地待在同一張表裡。下次再遇到要不要為了幾個欄位裝外掛這個問題,答案已經不會只是直覺反應,而是能具體算出成本落在哪一邊。

標籤
ACF 自訂欄位 meta box wp_postmeta register_post_meta
常見問答

本區問答由 AI 依文章內容自動整理,僅供快速參考,正式內容仍以全文為準。

meta box 跟 ACF 有什麼不同?

兩者做的事本質相同,都是包一層介面讓資料寫進同一張 wp_postmeta 資料表;差別在 ACF 已經把圖片、關聯文章、彈性內容、重複欄位這類進階型態都寫好了,後台點一點就能用,自己刻 meta box 則要自己寫表單與清理邏輯。

只有一兩個欄位需要裝 ACF 這類外掛嗎?

不一定,如果欄位型態單純、只給開發者自己用,且想少一個外掛相依,自己刻 meta box 成本更低;但若欄位要重複新增、需要彈性內容,或要讓不懂程式的同事天天填寫,外掛省下的力氣就值回票價。

meta box 的自訂欄位為什麼看不到?

只要 meta_key 以底線開頭,就不會出現在內建的自訂欄位面板清單裡,這是 WordPress 刻意的設計,用來避免使用者在陽春的兩欄位介面裡不小心把精心設計的欄位改壞;資料其實已經正常寫進資料庫,並非儲存失敗。

meta box 的欄位存值前要驗證哪些東西?

存值邏輯要先驗證 nonce 確認請求來自正確的編輯畫面,接著排除自動儲存避免舊資料覆蓋,再檢查使用者是否有權限編輯這篇文章,三關都通過才依欄位型態清理資料並寫入,任何一關失敗就要立刻中止。

meta box 資料能被 REST API 讀到嗎?

不會自動出現,後台編輯畫面能存取跟 REST API 能不能讀寫是兩件獨立的事,要另外呼叫 register_post_meta() 並把 show_in_rest 參數設為 true 才會出現在回應裡。

延伸閱讀

Advanced Custom Fields 是什麼?自訂欄位補上型別、驗證與版面

WordPress 原生自訂欄位只有陽春的鍵值輸入框,Advanced Custom Fields 用點選介面幫它補上型別、驗證與版面配置,35 種欄位型別裡免費版就能用 31 種,涵蓋位置規則、條件邏輯到區塊主題怎麼接資料。

Elementor 動態內容:欄位資料自動帶入教學

Elementor 動態內容能讓文章標題、特色圖、自訂欄位(含 ACF)自動帶入版面,一次排版就套用到全站文章。這篇教你怎麼綁定 Dynamic Tags、套進 Theme Builder 模板,並排查欄位抓不到資料的常見狀況。

AI寫WordPress程式碼安全嗎?先過這幾道判準

AI寫WordPress程式碼看起來能動、畫面正常,就代表安全嗎?研究顯示,用 AI 輔助寫程式的開發者反而更相信自己交出去的東西沒有問題,但實際漏洞卻更多。從輸入消毒、輸出逃逸、nonce 與權限判斷,到 SQL 查詢與套件名稱是否真的存在,把這幾道判準一一過一遍,才知道能不能放心讓 AI 代勞。

WordPress 是什麼?完整介紹與新手必懂重點

WordPress 是全球市占率最高的開源內容管理系統,能從部落格、品牌官網一路擴充到電商與會員系統。文章說明 WordPress 是什麼、2026 年 7.0 版本的 AI 新功能、費用與安全防護、SEO 表現,也拆解前後台速度差異、遇到問題有沒有人可以問,幫助你判斷這套系統能不能陪網站走得夠久。

網站升級後當機?PHP 8 相容問題這樣揪出兇手外掛

主機把 PHP 升到 8.x 後網站當機,很多人以為外掛壞了,其實問題出在 PHP 8 相容,舊外掛慣用的寫法被新版判定不合法。這篇整理怎麼用 debug.log 揪出真正犯規的外掛、分辨哪些錯誤會讓網站掛掉、哪些只是可先忽略的棄用警告,以及升級前怎麼用測試站把風險先排除掉。

讓每一個故事都有專屬的風格與形象。

龐果設計有限公司

統一編號:50947987

Quick Links

  • 品牌設計
  • 網頁設計
  • WordPress 服務
  • 設計案例
  • 關於我們
  • 觀點聚焦

Contact

地址:高雄市鼓山區內惟路435巷3號

電話:(07) 588-6337

Email:[email protected]

LINE:@imk3340l

FB IG X
© 2026 龐果設計 Pongo Design. All rights reserved.