2026年5月20日
2026年5月20日
WordPressのメタボックス追加方法|add_meta_boxと安全な保存処理(nonce・権限)
メタボックスは、投稿の編集画面に表示される入力欄のまとまりです。WordPress 本体の関数 add_meta_box() を使うと、ACF などのプラグインを使わずに、独自の入力欄を追加して投稿メタ(カスタムフィールド)に保存できます。
この記事では、投稿に「価格」「SKU」「在庫あり」の3項目を入力するメタボックスを、小さなプラグインとして作ります。コードは2026年11月時点の WordPress 7.1(PHP 8.3)で、登録・保存・nonce の検証が動くことを確かめました。
完成形のプラグイン
次のファイルを wp-content/plugins/myplugin-product-info/myplugin-product-info.php として保存し、「プラグイン」画面で有効化します。
<?php
/**
* Plugin Name: 商品情報メタボックス
* Description: 投稿の編集画面に「商品情報」のメタボックスを追加します。
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// 1. メタボックスを登録する
add_action( 'add_meta_boxes', 'myplugin_add_product_box' );
function myplugin_add_product_box() {
add_meta_box(
'myplugin_product_info', // ID(HTML の id にもなる。他と重ならない名前)
'商品情報', // 見出し
'myplugin_product_box_html', // 中身を出力する関数
array( 'post' ), // 表示する投稿タイプ(複数なら array( 'post', 'page' ))
'normal', // 位置: normal / side / advanced
'high' // 優先度: high / core / default / low
);
}
// 2. メタボックスの中身(入力欄)を出力する
function myplugin_product_box_html( $post ) {
wp_nonce_field( 'myplugin_save_product_info', 'myplugin_product_nonce' );
$price = get_post_meta( $post->ID, '_myplugin_price', true );
$sku = get_post_meta( $post->ID, '_myplugin_sku', true );
$in_stock = get_post_meta( $post->ID, '_myplugin_in_stock', true );
?>
<p>
<label for="myplugin_price">価格(円)</label><br>
<input type="number" id="myplugin_price" name="myplugin_price"
value="<?php echo esc_attr( $price ); ?>" min="0" step="1">
</p>
<p>
<label for="myplugin_sku">SKU</label><br>
<input type="text" id="myplugin_sku" name="myplugin_sku"
value="<?php echo esc_attr( $sku ); ?>" class="regular-text">
</p>
<p>
<label>
<input type="checkbox" name="myplugin_in_stock" value="1" <?php checked( $in_stock, '1' ); ?>>
在庫あり
</label>
</p>
<?php
}
// 3. 保存する(post 投稿タイプの保存時だけ動く)
add_action( 'save_post_post', 'myplugin_save_product_info' );
function myplugin_save_product_info( $post_id ) {
// このメタボックスのフォームから送られたときだけ処理する
if ( ! isset( $_POST['myplugin_product_nonce'] ) ) {
return;
}
$nonce = sanitize_text_field( wp_unslash( $_POST['myplugin_product_nonce'] ) );
if ( ! wp_verify_nonce( $nonce, 'myplugin_save_product_info' ) ) {
return;
}
// 自動保存とリビジョンでは何もしない
if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || wp_is_post_revision( $post_id ) ) {
return;
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
// 価格: 空欄ならメタを消す。数字は 0 以上の整数にする
$price = isset( $_POST['myplugin_price'] ) ? trim( wp_unslash( $_POST['myplugin_price'] ) ) : '';
if ( '' === $price ) {
delete_post_meta( $post_id, '_myplugin_price' );
} else {
update_post_meta( $post_id, '_myplugin_price', absint( $price ) );
}
// SKU: 1行のテキストとして無害化する
$sku = isset( $_POST['myplugin_sku'] ) ? sanitize_text_field( wp_unslash( $_POST['myplugin_sku'] ) ) : '';
update_post_meta( $post_id, '_myplugin_sku', $sku );
// チェックボックス: 外すと送信されないので、無ければ '0' を保存する
$in_stock = isset( $_POST['myplugin_in_stock'] ) ? '1' : '0';
update_post_meta( $post_id, '_myplugin_in_stock', $in_stock );
}
有効化して投稿の編集画面を開くと、本文の下に「商品情報」のメタボックスが表示されます。テーマの functions.php に書いても動きますが、テーマを変えると入力欄が消えるので、プラグインにしておくのがおすすめです。
add_meta_box の引数
add_meta_box( $id, $title, $callback, $screen, $context, $priority, $callback_args );
| 引数 | 内容 |
|---|---|
$id |
メタボックスの ID。HTML の id 属性にも使われるので、プラグイン名を付けて重ならないようにする |
$title |
メタボックスの見出し |
$callback |
中身を出力する関数。引数に投稿(WP_Post)が渡る |
$screen |
表示する画面。投稿タイプ名(post・page・カスタム投稿タイプ)を文字列か配列で指定 |
$context |
表示位置。normal(本文の下)・side(サイドバー)・advanced |
$priority |
同じ位置の中での順番。high・core・default・low |
$callback_args |
$callback に渡す追加の値(配列)。省略可 |
特定の投稿タイプだけで処理したいときは、add_meta_boxes の代わりに add_meta_boxes_post(post の部分は投稿タイプ名)というフックも使えます。
保存処理で確かめていること
save_post 系のフックは、投稿が保存されるたびに動きます。メタボックスのフォーム以外から保存された場合(クイック編集、REST API、プログラムからの wp_update_post() など)にも動くため、次の順で確認してから保存します。
- nonce があるか・正しいか:
wp_nonce_field()で出した値をwp_verify_nonce()で確かめます。nonce が無ければ、このメタボックスから送られた保存ではないので何もしません。これで、他の画面からの保存で値が消える事故と、外部サイトからの偽の送信(CSRF)を防げます。 - 自動保存・リビジョンではないか: 自動保存のときは入力欄の値が送られないことがあるので、処理しません。
- 権限があるか:
current_user_can( 'edit_post', $post_id )で、その投稿を編集できるユーザーか確かめます。
値は $_POST から取り出したあと、wp_unslash() で WordPress が付けたスラッシュを外し、項目に合った関数で無害化します。数値は absint()、1行のテキストは sanitize_text_field()、複数行のテキストなら sanitize_textarea_field()、URL なら esc_url_raw() です。
フックを save_post_post にしているのは、post 投稿タイプの保存時だけ動かすためです。固定ページにも使う場合は save_post_page にも同じ関数を登録するか、save_post を使って関数内で投稿タイプを確かめます。
チェックボックスの扱い
チェックを外したチェックボックスは、フォームの送信内容に含まれません。「送られてきたら保存する」書き方だと、一度チェックを付けた投稿で外せなくなります。上のコードのように、送られてこなければ '0' を保存します。
ブロックエディターでの動き
ブロックエディターでも、add_meta_box() で追加したメタボックスは本文の下に表示されます。投稿を保存すると、本文の保存(REST API)のあとに、メタボックスのフォームが別に送信されます。そのため save_post_post は1回の保存で2回動きますが、メタボックスの値が入っているのは2回目だけです。1回目は nonce が無いので何もせずに戻ります。
メタキーの先頭のアンダースコア
メタキーを _myplugin_price のように _ で始めると、WordPress はそれを「保護されたメタ」として扱い、編集画面の「カスタムフィールド」欄に表示しません。メタボックスで入力する値を、カスタムフィールド欄から二重に編集されないようにするためです。カスタムフィールド欄からも見せたい場合は、_ を付けないキーにします。
テンプレートで表示する
保存した値は get_post_meta() で取り出します。出力するときは esc_html() などでエスケープします。
<?php
// テンプレート(single.php など)のループ内
$price = get_post_meta( get_the_ID(), '_myplugin_price', true );
if ( '' !== $price ) {
echo '<p>価格: ' . esc_html( number_format( (int) $price ) ) . '円</p>';
}
第3引数を true にすると、値が1つの文字列で返ります(保存されていなければ空文字)。
メタボックスの出し分けと削除
管理者にだけ表示したいメタボックスは、登録する前に権限を確かめます。不要なメタボックスは remove_meta_box() で消せます。
<?php
// 管理者にだけ、サイドバーに小さなメタボックスを出す
add_action( 'add_meta_boxes_post', function ( $post ) {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
add_meta_box( 'myplugin_admin_note', '管理者メモ', 'myplugin_admin_note_html', 'post', 'side' );
} );
// クラシックエディターの「カスタムフィールド」欄を消す
add_action( 'add_meta_boxes', function () {
remove_meta_box( 'postcustom', 'post', 'normal' );
}, 20 );
1つ目の例では、myplugin_admin_note_html という出力用の関数を別に用意します。
remove_meta_box() が効くのは、メタボックスとして登録されたものだけです。ブロックエディターのサイドバーにある「ディスカッション」「抜粋」などはエディター側のパネルで、WordPress 本体はこれらのメタボックスをブロックエディターでは表示しません。そのため、remove_meta_box() ではブロックエディターのパネルは消えません。
WP-CLI で保存結果を確かめる
# 投稿 123 のメタをすべて表示(_ で始まるキーも出る)
wp post meta list 123 --format=table
# 1つの値を取り出す
wp post meta get 123 _myplugin_price
# テスト用に値を書き換える
wp post meta update 123 _myplugin_price 9800
# 特定の値を持つ投稿を探す
wp post list --meta_key=_myplugin_sku --meta_value=AB-1 --fields=ID,post_title
うまく保存されないとき
- 値が保存されない:
wp_nonce_field()とwp_verify_nonce()で、アクション名(第1引数)が同じか確かめます。nonce のフィールド名(wp_nonce_field()の第2引数)と$_POSTのキーが一致しているかも見ます。 - 一部の投稿タイプで保存されない:
save_post_postはpost専用です。登録した投稿タイプに合わせてフックを追加します。 - カスタム投稿タイプでメタボックスが出ない:
add_meta_box()の第4引数に、その投稿タイプ名を入れているか確かめます。
ACF などのプラグインとの使い分けや、カスタムフィールドの保存トラブルは次の記事も参考にしてください。