2026年5月20日

2026年5月20日

WordPressにカスタムメタボックスを追加する方法

はじめに

メタボックスは投稿編集画面に追加できるUIパネルです。add_meta_box でカスタムフィールドの入力エリアを作り、save_post フックで値を保存することで、タイトル・本文以外の追加情報(イベント日時・価格・外部URLなど)を管理できます。

症状・原因

  • 投稿にカスタムデータ(日付・価格・URL)を追加したい
  • カスタムフィールドの入力UIを整えたい
  • プラグインなしでメタデータを管理したい
  • 特定の投稿タイプの編集画面にのみメタボックスを表示したい

解決手順

ステップ1:メタボックスを登録する

// functions.php
function mytheme_add_meta_boxes(): void {
    add_meta_box(
        'mytheme_event_details',         // メタボックスID
        'イベント詳細',                    // タイトル
        'mytheme_event_details_callback', // コールバック関数
        'event',                          // 投稿タイプ(nullで全タイプ)
        'normal',                         // 位置: normal / side / advanced
        'high'                            // 優先度: high / default / low
    );
}
add_action('add_meta_boxes', 'mytheme_add_meta_boxes');

ステップ2:メタボックスのHTMLを出力する

// functions.php
function mytheme_event_details_callback(WP_Post $post): void {
    // nonceフィールドを出力(CSRF対策)
    wp_nonce_field('mytheme_event_details_nonce', 'mytheme_event_nonce');

    // 既存の値を取得
    $event_date  = get_post_meta($post->ID, '_event_date', true);
    $event_place = get_post_meta($post->ID, '_event_place', true);
    $event_price = get_post_meta($post->ID, '_event_price', true);
    $event_url   = get_post_meta($post->ID, '_event_url', true);
    ?>
    <table class="form-table">
        <tr>
            <th scope="row">
                <label for="event_date">開催日時</label>
            </th>
            <td>
                <input
                    type="datetime-local"
                    id="event_date"
                    name="event_date"
                    value="<?php echo esc_attr($event_date); ?>"
                    class="regular-text"
                >
            </td>
        </tr>
        <tr>
            <th scope="row">
                <label for="event_place">開催場所</label>
            </th>
            <td>
                <input
                    type="text"
                    id="event_place"
                    name="event_place"
                    value="<?php echo esc_attr($event_place); ?>"
                    class="regular-text"
                    placeholder="東京都渋谷区〇〇"
                >
            </td>
        </tr>
        <tr>
            <th scope="row">
                <label for="event_price">参加費(円)</label>
            </th>
            <td>
                <input
                    type="number"
                    id="event_price"
                    name="event_price"
                    value="<?php echo esc_attr($event_price); ?>"
                    min="0"
                    step="100"
                    class="small-text"
                >
                <p class="description">無料の場合は 0 を入力してください。</p>
            </td>
        </tr>
        <tr>
            <th scope="row">
                <label for="event_url">申込URL</label>
            </th>
            <td>
                <input
                    type="url"
                    id="event_url"
                    name="event_url"
                    value="<?php echo esc_url($event_url); ?>"
                    class="regular-text"
                    placeholder="https://"
                >
            </td>
        </tr>
    </table>
    <?php
}

ステップ3:save_postで値を保存する

// functions.php
function mytheme_save_event_details(int $post_id): void {
    // ①自動保存時はスキップ
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    // ②権限チェック
    if (!current_user_can('edit_post', $post_id)) {
        return;
    }

    // ③nonce検証
    if (!isset($_POST['mytheme_event_nonce']) ||
        !wp_verify_nonce($_POST['mytheme_event_nonce'], 'mytheme_event_details_nonce')) {
        return;
    }

    // ④値のサニタイズと保存
    if (isset($_POST['event_date'])) {
        $date = sanitize_text_field($_POST['event_date']);
        update_post_meta($post_id, '_event_date', $date);
    }

    if (isset($_POST['event_place'])) {
        update_post_meta($post_id, '_event_place', sanitize_text_field($_POST['event_place']));
    }

    if (isset($_POST['event_price'])) {
        update_post_meta($post_id, '_event_price', absint($_POST['event_price']));
    }

    if (isset($_POST['event_url'])) {
        update_post_meta($post_id, '_event_url', esc_url_raw($_POST['event_url']));
    }
}
add_action('save_post_event', 'mytheme_save_event_details'); // 特定の投稿タイプのみ

ステップ4:テンプレートでメタデータを表示する

// single-event.php
$event_date  = get_post_meta(get_the_ID(), '_event_date', true);
$event_place = get_post_meta(get_the_ID(), '_event_place', true);
$event_price = get_post_meta(get_the_ID(), '_event_price', true);
$event_url   = get_post_meta(get_the_ID(), '_event_url', true);
?>
<dl class="event-details">
    <?php if ($event_date) : ?>
        <dt>開催日時</dt>
        <dd><time datetime="<?php echo esc_attr($event_date); ?>"><?php echo esc_html(date_i18n('Y年n月j日 H:i', strtotime($event_date))); ?></time></dd>
    <?php endif; ?>

    <?php if ($event_place) : ?>
        <dt>開催場所</dt>
        <dd><?php echo esc_html($event_place); ?></dd>
    <?php endif; ?>

    <?php if ($event_price !== '') : ?>
        <dt>参加費</dt>
        <dd><?php echo $event_price > 0 ? number_format((int)$event_price) . '円' : '無料'; ?></dd>
    <?php endif; ?>

    <?php if ($event_url) : ?>
        <dt>申し込み</dt>
        <dd><a href="<?php echo esc_url($event_url); ?>" target="_blank" rel="noopener">申し込みはこちら</a></dd>
    <?php endif; ?>
</dl>

注意事項

  • メタキーをアンダースコア(_)で始めると、標準の「カスタムフィールド」メタボックスに表示されなくなります(プライベートメタ)。意図的に非表示にしたい場合に使います
  • save_post ではなく save_post_{post_type} を使うと特定の投稿タイプのみに処理が限定されてセキュリティが向上します
  • ブロックエディター(Gutenberg)ではメタボックスは「互換性のためのモード」として動作します。本格的なブロックエディター統合には register_meta + サイドバープラグインの実装を推奨します

まとめ

add_meta_box でパネルを登録し、コールバックでnonce + フォームフィールドを出力します。save_post_{post_type} で自動保存除外・権限確認・nonce検証の3ステップを必ず行ってから update_post_meta で保存します。テンプレートでは get_post_meta($id, 'key', true) で取得します。

お気軽にご相談ください

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