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() など)にも動くため、次の順で確認してから保存します。

  1. nonce があるか・正しいか: wp_nonce_field() で出した値を wp_verify_nonce() で確かめます。nonce が無ければ、このメタボックスから送られた保存ではないので何もしません。これで、他の画面からの保存で値が消える事故と、外部サイトからの偽の送信(CSRF)を防げます。
  2. 自動保存・リビジョンではないか: 自動保存のときは入力欄の値が送られないことがあるので、処理しません。
  3. 権限があるか: 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 などのプラグインとの使い分けや、カスタムフィールドの保存トラブルは次の記事も参考にしてください。

関連記事

お気軽にご相談ください

お見積りへ お問い合わせへ
▲