2026年8月30日
2026年8月30日
WordPressのCMB2でメタボックスを作る実践ガイド:登録・繰り返しグループ・表示まで
CMB2 は、投稿編集画面などにメタボックス(カスタムフィールドの入力欄)を PHP のコードで追加するためのライブラリです。cmb2_admin_init フックの中で new_cmb2_box() を呼び、add_field() で入力欄を足すだけで、nonce の検証や保存処理は CMB2 が受け持ちます。この記事では、イベント情報の入力欄を例に、メタボックスの登録、繰り返しグループ、設定ページ、テンプレートでの表示、つまずきやすい点までを通して説明します。
CMB2 を使う前に確認すること
CMB2 は WordPress.org でプラグインとして配布されており、Composer(パッケージ名 cmb2/cmb2)でテーマやプラグインに同梱することもできます。どちらの方法でも、フィールドの定義は自分で書く PHP コードです。管理画面からフィールドを作る GUI はありません。
更新の頻度は時期によってまちまちなので、採用前にプラグインページの最終更新日と、使っている WordPress・PHP のバージョンで問題が報告されていないかを確認してください(2026年10月現在)。新規のサイトで長く使う前提なら、ACF やコアの register_post_meta() と比べて判断する価値があります。
定義を書く場所は、テーマではなくサイト専用プラグインか mu-plugin を勧めます。テーマを変えると入力欄が消え、データはあっても編集できなくなるためです。作業はステージング環境で、データベースのバックアップを取ってから行ってください。
CMB2 を読み込む
プラグインとして有効化した場合は、そのまま次の章のコードを書けます。Composer で同梱した場合は、CMB2 の init.php を読み込みます。
<?php
// サイト専用プラグインのメインファイル(例)
if ( file_exists( __DIR__ . '/vendor/cmb2/cmb2/init.php' ) ) {
require_once __DIR__ . '/vendor/cmb2/cmb2/init.php';
}
CMB2 は複数のプラグインが別々に同梱していても、最も新しいバージョンだけが読み込まれる仕組みを持っています。同梱した CMB2 が使われていないように見えるときは、ほかのプラグインがより新しい版を持っていないかを確認します。
メタボックスを登録する
ここでは event という投稿タイプに、開催日・会場・申込 URL の入力欄を付けます。
<?php
add_action( 'cmb2_admin_init', 'myprefix_register_event_metabox' );
function myprefix_register_event_metabox() {
$prefix = '_myprefix_event_';
$cmb = new_cmb2_box( array(
'id' => 'myprefix_event_details',
'title' => 'イベント情報',
'object_types' => array( 'event' ),
'context' => 'normal',
'priority' => 'high',
'show_names' => true,
) );
$cmb->add_field( array(
'name' => '開催日時',
'id' => $prefix . 'start',
'type' => 'text_datetime_timestamp',
) );
$cmb->add_field( array(
'name' => '会場',
'id' => $prefix . 'venue',
'type' => 'text',
) );
$cmb->add_field( array(
'name' => '申込URL',
'id' => $prefix . 'url',
'type' => 'text_url',
'protocols' => array( 'https' ),
) );
}
id がそのままメタキーになります。先頭をアンダースコアにすると、WordPress 標準の「カスタムフィールド」パネルに表示されない保護されたメタになり、CMB2 の入力欄だけで編集されるようになります。接頭辞を付けておくのは、ほかのプラグインとのキー衝突を避けるためです。
日付の型は保存形式で選びます。text_date は date_format で指定した文字列として保存され、text_date_timestamp や text_datetime_timestamp は UNIX タイムスタンプとして保存されます。開催日順に並べたり、終了したイベントを除いたりしたいなら、数値比較ができるタイムスタンプ型が扱いやすくなります。
繰り返しグループを作る
登壇者のように件数が決まっていない情報は group 型で作ります。
<?php
// myprefix_register_event_metabox() の中に追記
$group_id = $cmb->add_field( array(
'id' => $prefix . 'speakers',
'type' => 'group',
'options' => array(
'group_title' => '登壇者 {#}',
'add_button' => '登壇者を追加',
'remove_button' => '削除',
'sortable' => true,
),
) );
$cmb->add_group_field( $group_id, array(
'name' => '名前',
'id' => 'name',
'type' => 'text',
) );
$cmb->add_group_field( $group_id, array(
'name' => '所属',
'id' => 'org',
'type' => 'text',
) );
グループは1つのメタキーに、行ごとの配列をまとめてシリアライズした形で保存されます。そのためグループの中の値を meta_query で検索・並べ替えすることはできません。絞り込みに使う値はグループに入れず、単独のフィールドにしておきます。
権限で表示を絞る・独自の検証を足す
CMB2 は保存時に nonce を検証し、フィールドの型に応じたサニタイズを行います。それでも、特定の権限を持つ人だけに入力欄を見せたい、値の形式を独自に確かめたい、といった要件は自分で書きます。
<?php
$cmb->add_field( array(
'name' => '社内メモ',
'id' => $prefix . 'internal_note',
'type' => 'textarea_small',
'show_on_cb' => function () {
return current_user_can( 'edit_others_posts' );
},
'sanitization_cb' => function ( $value ) {
return sanitize_textarea_field( $value );
},
) );
show_on_cb は入力欄を出すかどうか、sanitization_cb は保存前の値の整形を担当します。表示の可否だけでは保存の可否まで制御できない場面があるため、重要な値は保存側でも検証してください。
設定ページを作る
サイト全体の設定を1か所で管理したい場合は、object_types に options-page を指定します。
<?php
add_action( 'cmb2_admin_init', function () {
$options = new_cmb2_box( array(
'id' => 'myprefix_site_options',
'title' => 'サイト設定',
'object_types' => array( 'options-page' ),
'option_key' => 'myprefix_options',
'capability' => 'manage_options',
) );
$options->add_field( array(
'name' => '告知バナーの文言',
'id' => 'banner_text',
'type' => 'text',
) );
} );
値は option_key の名前で1つのオプションにまとめて保存され、cmb2_get_option( 'myprefix_options', 'banner_text' ) で取り出せます。
テンプレートで表示する
CMB2 のメタボックスに入れた値は通常の投稿メタなので、get_post_meta() で読めます。出力時のエスケープは自分で行います。
<?php
$post_id = get_the_ID();
$start = get_post_meta( $post_id, '_myprefix_event_start', true );
$venue = get_post_meta( $post_id, '_myprefix_event_venue', true );
$url = get_post_meta( $post_id, '_myprefix_event_url', true );
$speakers = get_post_meta( $post_id, '_myprefix_event_speakers', true );
if ( $start ) {
echo '<p>開催: ' . esc_html( wp_date( 'Y年n月j日 H:i', (int) $start ) ) . '</p>';
}
if ( $venue ) {
echo '<p>会場: ' . esc_html( $venue ) . '</p>';
}
if ( $url ) {
echo '<p><a href="' . esc_url( $url ) . '">申し込みはこちら</a></p>';
}
if ( is_array( $speakers ) ) {
echo '<ul>';
foreach ( $speakers as $speaker ) {
$name = isset( $speaker['name'] ) ? $speaker['name'] : '';
$org = isset( $speaker['org'] ) ? $speaker['org'] : '';
echo '<li>' . esc_html( $name ) . ( $org ? '(' . esc_html( $org ) . ')' : '' ) . '</li>';
}
echo '</ul>';
}
グループの各行は、入力されなかった子フィールドのキーが存在しないことがあります。isset() で確かめてから使うと、PHP の警告を避けられます。
つまずきやすい点
- メタボックスが出ない:
object_typesの投稿タイプ名の綴り、CMB2 が読み込まれているか、フックがcmb2_admin_initか(フロント側のフォームでも使うならcmb2_init)を順に確認します - ブロックエディターでの位置: メタボックスは本文の下の領域に表示されます。サイドバーには出ないため、編集者に場所を伝えておきます
- REST API で値が見えない: 投稿メタは既定では REST API に含まれません。CMB2 のボックス設定で REST API への公開を指定する方法がありますが、公開した値は誰でも読める可能性があるので、社内メモなどは含めないでください
- ID を後から変えた: 既存の値は古いメタキーに残ったままです。変更する場合は WP-CLI などでメタキーを移すスクリプトを用意してから切り替えます
まとめ
- CMB2 は
cmb2_admin_initでnew_cmb2_box()とadd_field()を呼ぶだけで、nonce と保存処理を任せられる idがメタキーになる。接頭辞とアンダースコアで衝突と二重編集を防ぐ- 並べ替えや絞り込みに使う値はタイムスタンプ型や単独フィールドにし、グループには入れない
- 権限は
show_on_cb、検証はsanitization_cbで足し、出力はesc_html()/esc_url()でエスケープする - 定義はサイト専用プラグインか mu-plugin に置き、更新状況を確認してから採用する