2026年5月17日

2026年5月17日

WordPressのカスタムフィールドをプラグインなしで実装する方法

はじめに

「ACFを使わずにカスタムフィールドを自前実装したい」「メタボックスで管理画面に独自の入力フィールドを追加したい」「プラグインへの依存を減らしてシンプルに運用したい」——add_meta_box()を使えばACFなしでも柔軟なカスタムフィールドが実装できます。

症状・原因

ACFや似たプラグインは手軽ですが、追加のDBクエリや管理画面JS読み込みが発生します。投稿タイプごとに少数の固定フィールドだけ必要な場合は、add_meta_box()update_post_meta()で自前実装した方が軽量です。

解決手順

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

// functions.php: カスタムフィールド用メタボックスを追加
add_action( 'add_meta_boxes', function() {
    add_meta_box(
        'my_product_meta',           // メタボックスのID
        '商品情報',                   // タイトル
        'my_product_meta_callback',  // コールバック関数
        'product',                   // 投稿タイプ('post'・'page'・カスタム投稿)
        'normal',                    // 位置(normal/side/advanced)
        'high'                       // 優先度(high/core/default/low)
    );
} );

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

// functions.php: メタボックスのコールバック関数
function my_product_meta_callback( $post ) {
    // nonceフィールドでCSRF対策
    wp_nonce_field( 'my_product_meta_save', 'my_product_meta_nonce' );

    // 既存の値を取得
    $price    = get_post_meta( $post->ID, '_product_price', true );
    $sku      = get_post_meta( $post->ID, '_product_sku', true );
    $in_stock = get_post_meta( $post->ID, '_product_in_stock', true );
    $note     = get_post_meta( $post->ID, '_product_note', true );
    ?>
    <table class="form-table">
        <tr>
            <th><label for="product_price">価格(円)</label></th>
            <td>
                <input type="number" id="product_price" name="product_price"
                       value="<?php echo esc_attr( $price ); ?>"
                       min="0" step="1" class="regular-text">
            </td>
        </tr>
        <tr>
            <th><label for="product_sku">SKU(商品コード)</label></th>
            <td>
                <input type="text" id="product_sku" name="product_sku"
                       value="<?php echo esc_attr( $sku ); ?>"
                       class="regular-text" placeholder="例:PROD-001">
            </td>
        </tr>
        <tr>
            <th>在庫状況</th>
            <td>
                <label>
                    <input type="checkbox" name="product_in_stock" value="1"
                           <?php checked( $in_stock, '1' ); ?>>
                    在庫あり
                </label>
            </td>
        </tr>
        <tr>
            <th><label for="product_note">備考</label></th>
            <td>
                <textarea id="product_note" name="product_note"
                          rows="4" class="large-text"><?php echo esc_textarea( $note ); ?></textarea>
            </td>
        </tr>
    </table>
    <?php
}

ステップ3:save_postでサニタイズして保存する

// functions.php: 投稿保存時にカスタムフィールドを保存
add_action( 'save_post_product', function( $post_id ) {
    // 自動保存・リビジョンはスキップ
    if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
        return;
    }

    // nonceの検証(CSRF対策)
    if ( ! isset( $_POST['my_product_meta_nonce'] )
        || ! wp_verify_nonce( $_POST['my_product_meta_nonce'], 'my_product_meta_save' ) ) {
        return;
    }

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

    // 価格(整数)
    if ( isset( $_POST['product_price'] ) ) {
        update_post_meta( $post_id, '_product_price', absint( $_POST['product_price'] ) );
    }

    // SKU(英数字・ハイフン・アンダースコアのみ許可)
    if ( isset( $_POST['product_sku'] ) ) {
        $sku = sanitize_text_field( $_POST['product_sku'] );
        $sku = preg_replace( '/[^A-Za-z0-9\-_]/', '', $sku );
        update_post_meta( $post_id, '_product_sku', $sku );
    }

    // 在庫(チェックボックス)
    $in_stock = isset( $_POST['product_in_stock'] ) ? '1' : '0';
    update_post_meta( $post_id, '_product_in_stock', $in_stock );

    // 備考(テキストエリア)
    if ( isset( $_POST['product_note'] ) ) {
        update_post_meta( $post_id, '_product_note', sanitize_textarea_field( $_POST['product_note'] ) );
    }
} );

ステップ4:フロントエンドでカスタムフィールドを表示する

<?php
// single-product.php: カスタムフィールドを表示
$post_id  = get_the_ID();
$price    = get_post_meta( $post_id, '_product_price', true );
$sku      = get_post_meta( $post_id, '_product_sku', true );
$in_stock = get_post_meta( $post_id, '_product_in_stock', true );
$note     = get_post_meta( $post_id, '_product_note', true );
?>
<div class="product-meta">
    <?php if ( $price ) : ?>
        <p class="product-price">
            <span class="label">価格:</span>
            <strong class="price-value">¥<?php echo number_format( (int) $price ); ?></strong>
        </p>
    <?php endif; ?>

    <?php if ( $sku ) : ?>
        <p class="product-sku">
            <span class="label">商品コード:</span>
            <code><?php echo esc_html( $sku ); ?></code>
        </p>
    <?php endif; ?>

    <p class="product-stock <?php echo $in_stock ? 'in-stock' : 'out-of-stock'; ?>">
        <?php echo $in_stock ? '✓ 在庫あり' : '✗ 在庫なし'; ?>
    </p>

    <?php if ( $note ) : ?>
        <div class="product-note">
            <p><?php echo nl2br( esc_html( $note ) ); ?></p>
        </div>
    <?php endif; ?>
</div>

ステップ5:WP_Queryでカスタムフィールドを検索条件に使う

// カスタムフィールドの値で投稿を絞り込む
$products_in_stock = new WP_Query( [
    'post_type'  => 'product',
    'meta_query' => [
        [
            'key'     => '_product_in_stock',
            'value'   => '1',
            'compare' => '=',
        ],
    ],
    'meta_key'   => '_product_price',
    'orderby'    => 'meta_value_num',
    'order'      => 'ASC',
] );

// 価格範囲で絞り込み
$products_range = new WP_Query( [
    'post_type'  => 'product',
    'meta_query' => [
        [
            'key'     => '_product_price',
            'value'   => [ 1000, 5000 ],
            'type'    => 'NUMERIC',
            'compare' => 'BETWEEN',
        ],
    ],
] );

注意事項

  • wp_nonce_field()wp_verify_nonce()によるCSRF対策は必須です。省略するとXSRF攻撃に脆弱になります。
  • メタキーの先頭にアンダースコア(_product_price)を付けると、デフォルトの「カスタムフィールド」パネルから非表示になります。管理画面のUIをメタボックスのみに限定したい場合に有効です。
  • save_post_{post_type}フックを使うと、特定の投稿タイプの保存時のみ処理が実行されます。save_postだと全投稿タイプで発火するため非効率です。

まとめ

カスタムフィールドのプラグインなし実装は「add_meta_box()でメタボックスを登録→コールバック関数でHTMLを出力(nonce付き)→save_post_{type}フックでnonce検証・サニタイズ・update_post_meta()で保存→get_post_meta()でフロントエンドに表示→meta_queryで絞り込み検索」の流れで整備します。関連記事:Advanced Custom Fields(ACF)でカスタムフィールドを管理する方法WordPressのカスタム投稿タイプを作成する方法

お気軽にご相談ください

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