2026年6月16日
2026年6月16日
WordPressのカスタムフィールドをメタボックス(add_meta_box)で管理画面に追加する手順
プラグインを使わずに投稿編集画面へ独自の入力欄を追加するには、add_meta_boxes フックで add_meta_box() を呼んで入力欄を表示し、save_post フックで値を検証して update_post_meta() で保存します。この記事では「価格」を入力するメタボックスを例に、表示・保存・フロントへの出力までを一通り実装し、nonceと権限チェック、ブロックエディターでの保存のタイミングといった、つまずきやすい点を説明します。
メタボックスとカスタムフィールドの関係
カスタムフィールドは、投稿ごとに追加情報を保存する仕組みで、データは wp_postmeta テーブルに「投稿ID・キー・値」の組で入ります。WordPressには標準の「カスタムフィールド」パネルもありますが、キー名を毎回手入力する必要があり、入力ミスが起きやすく、値の形式も制限できません。
メタボックスは、このカスタムフィールドを専用の入力欄として編集画面に表示するための仕組みです。ラベル付きの入力欄にしておけば、編集者はキー名を意識せずに値を入れられ、保存時に数値だけを受け付けるといった検証もできます。
コードを書く場所
メタボックスのコードは、子テーマの functions.php か、小さな自作プラグインに書きます。親テーマの functions.php に直接書くと、テーマ更新で消えてしまいます。
ただし、カスタムフィールドに保存したデータはテーマを切り替えても残ります。テーマを変えると入力欄だけが消える、という状態を避けたいなら、wp-content/plugins/ に専用プラグインを作るほうが扱いやすくなります。どちらの場合も、本番サイトで試す前にバックアップを取り、できればステージング環境で動作を確認してください。functions.php の文法エラーは、サイト全体が表示されなくなる原因になります。
手順1: メタボックスを登録する
add_meta_boxes フックの中で add_meta_box() を呼びます。
<?php
add_action( 'add_meta_boxes', 'mysite_add_price_meta_box' );
function mysite_add_price_meta_box() {
add_meta_box(
'mysite_price', // メタボックスのID(HTMLのidにもなる)
'価格情報', // 見出し
'mysite_render_price_meta_box', // 中身を出力するコールバック
'post', // 表示する投稿タイプ
'side', // 表示位置: normal / side / advanced
'default' // 優先度: high / core / default / low
);
}
第4引数には投稿タイプを指定します。固定ページなら 'page'、カスタム投稿タイプなら登録したスラッグ、複数に出したいなら array( 'post', 'page' ) のように配列で渡せます。第5引数の side はサイドバー、normal と advanced は本文の下に表示されます。
手順2: 入力欄を出力する
コールバック関数は、編集中の投稿オブジェクトを受け取ります。保存済みの値を読み出して入力欄に表示し、あわせてnonceのフィールドを出力します。
function mysite_render_price_meta_box( $post ) {
wp_nonce_field( 'mysite_save_price', 'mysite_price_nonce' );
$price = get_post_meta( $post->ID, '_mysite_price', true );
?>
<p>
<label for="mysite_price_field">税込価格(円)</label>
<input type="number" id="mysite_price_field" name="mysite_price_field"
value="<?php echo esc_attr( $price ); ?>" min="0" step="1" class="widefat">
</p>
<?php
}
ポイントは3つあります。
get_post_meta()の第3引数をtrueにすると、配列ではなく単一の値が返ります。- 値を
value属性に出すときはesc_attr()でエスケープします。保存時に検証していても、出力時のエスケープは省略しません。 - メタキーを
_mysite_priceのようにアンダースコアで始めると「保護されたメタ」として扱われ、標準のカスタムフィールドパネルに表示されなくなります。同じ値が2か所で編集できてしまう混乱を防げます。
キー名には、他のテーマやプラグインと衝突しないよう、自分のサイト固有の接頭辞を付けてください。
手順3: 保存処理を書く
保存は save_post フックで行います。投稿タイプを絞りたい場合は save_post_{投稿タイプ} という動的フックが使えるので、ここでは save_post_post を使います。
add_action( 'save_post_post', 'mysite_save_price_meta' );
function mysite_save_price_meta( $post_id ) {
// nonceの検証: このフォームから送信されたものか
if ( ! isset( $_POST['mysite_price_nonce'] ) ||
! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['mysite_price_nonce'] ) ), 'mysite_save_price' ) ) {
return;
}
// 自動保存とリビジョンでは処理しない
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( wp_is_post_revision( $post_id ) ) {
return;
}
// 権限チェック: この投稿を編集できるユーザーか
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
if ( isset( $_POST['mysite_price_field'] ) && '' !== $_POST['mysite_price_field'] ) {
$price = absint( wp_unslash( $_POST['mysite_price_field'] ) );
update_post_meta( $post_id, '_mysite_price', $price );
} else {
delete_post_meta( $post_id, '_mysite_price' );
}
}
それぞれのチェックには理由があります。
nonce: 外部サイトから管理者のブラウザを使って不正な保存を送らせる攻撃(CSRF)を防ぎます。また、このメタボックスを含まない画面(クイック編集など)から save_post が呼ばれたときに、値を誤って空にしてしまうことも防げます。
自動保存とリビジョン: 自動保存のリクエストでは入力欄の値が送られないことがあり、処理すると値が消える原因になります。
権限: nonceは「正しいフォームから来た」ことの確認であり、「その人に権限がある」ことの確認ではありません。current_user_can( 'edit_post', $post_id ) で別途確かめます。
サニタイズ: 価格は0以上の整数なので absint() で整数に変換しています。文字列なら sanitize_text_field()、複数行なら sanitize_textarea_field()、URLなら esc_url_raw() のように、値の種類に合った関数を選びます。空欄で保存されたときは delete_post_meta() で削除し、空の行をDBに残さないようにしています。
ブロックエディターでの動き
ブロックエディターでもメタボックスは表示されます。side を指定したものは右側の設定サイドバーに、normal や advanced は本文エリアの下に表示されます。
保存の仕組みには注意が必要です。ブロックエディターは、まず投稿本体をREST API経由で保存し、その後でメタボックスの入力内容を別のリクエストで送信します。そのため1回の「更新」で save_post が2回動くことがあります。最初のRESTの保存にはメタボックスのnonceが含まれないので、上のコードではそこで処理が止まり、2回目のリクエストで値が保存されます。nonceチェックを入れずに「値がなければ削除」という処理を書くと、1回目で値が消えてしまうことがあるのはこのためです。
メタボックスが表示されない場合は、編集画面右上のオプションメニューにある設定画面(パネルの表示・非表示を切り替える項目)で、該当のメタボックスが非表示になっていないかを確認してください。メニューの表記はWordPressのバージョンによって異なります(2026年10月現在)。
手順4: フロントエンドに表示する
テーマのテンプレート(子テーマの single.php など)で値を取り出して表示します。
<?php
$price = get_post_meta( get_the_ID(), '_mysite_price', true );
if ( '' !== $price ) :
?>
<p class="mysite-price">価格: <?php echo esc_html( number_format_i18n( (int) $price ) ); ?>円</p>
<?php
endif;
値がない投稿では何も表示しないように、空文字かどうかを確認しています。出力時は esc_html() でエスケープします。
動作確認のチェックリスト
- 投稿を新規作成し、価格を入れて保存した後、再読み込みしても値が残っているか
- 価格を空にして保存したとき、値が削除されるか
- クイック編集で投稿を更新しても、価格が消えないか
- 投稿者など権限の低いユーザーで、他人の投稿の値を変更できないか
- フロントエンドで表示が正しく、値のない投稿では何も出ないか
値を入力欄ではなくブロックエディターのサイドバーに統合したい、REST APIからも読み書きしたい、という場合は、register_post_meta() で show_in_rest を有効にしてメタを登録し、JavaScriptでエディターのパネルを作る方法もあります。まずは本記事のPHPだけの方法で動く状態を作り、必要になったら移行を検討するとよいでしょう。
まとめ
- メタボックスは
add_meta_boxesフックでadd_meta_box()を呼んで登録する - 入力欄の出力ではnonceを出し、保存済みの値は
esc_attr()でエスケープして表示する - 保存は
save_post(またはsave_post_{投稿タイプ})で、nonce・自動保存・権限を確認してからサニタイズして保存する - メタキーはアンダースコア始まりにすると標準のカスタムフィールドパネルに出ない
- ブロックエディターではメタボックスが別リクエストで保存されるため、nonceチェックが値の消失防止にも役立つ
- コードは子テーマか自作プラグインに書き、事前にバックアップを取る