2026年6月27日

2026年6月27日

WooCommerceの注文詳細ページにカスタムフィールドを追加する方法(チェックアウト入力・管理画面・メール表示まで)

WooCommerce の注文に独自の項目(配送の要望、領収書の宛名、法人名など)を持たせるには、「チェックアウトで入力させる」「注文メタとして保存する」「管理画面・お客様の注文詳細・メールに表示する」の3段階を順に実装します。この記事では、クラシックのチェックアウトとブロックのチェックアウトの違いを押さえたうえで、HPOS(高性能注文ストレージ)でも壊れない書き方で注文詳細ページに項目を追加する手順を説明します。

最初に確認すること:チェックアウトの種類と注文の保存方式

コードを書く前に、次の2点を管理画面で確認してください。ここを取り違えると「コードを入れたのに入力欄が出ない」「保存されたはずの値が読めない」という状態になります。

1つ目はチェックアウトページの種類です。固定ページ「チェックアウト」を編集画面で開き、中身が「チェックアウト」ブロックなのか、[woocommerce_checkout] ショートコード(クラシック)なのかを見ます。新しめのバージョンで新規に作ったストアはブロックになっていることが多く、ブロックのチェックアウトでは従来の woocommerce_checkout_fields フィルターや woocommerce_after_order_notes アクションで足した入力欄は表示されません。

2つ目は注文データの保存方式です。WooCommerce の設定画面の「高度な設定」→「機能」付近(表記はバージョンにより異なります)で、注文データが HPOS(専用の注文テーブル)か、従来の投稿テーブルかを確認できます。HPOS が有効だと、注文は wp_posts / wp_postmeta ではなく専用テーブルに入るため、get_post_meta() や update_post_meta() で注文の値を読み書きするコードは期待どおりに動きません。どちらの方式でも動かすには、WC_Order オブジェクトの get_meta() / update_meta_data() / save() を使います。この記事のコードはすべてこの書き方にそろえています。

作業はステージング環境で行い、本番に入れる前にデータベースのバックアップを取ってください。コードは子テーマの functions.php か、wp-content/mu-plugins/ に置く小さなプラグインファイルに書きます。テーマを変えても注文データの扱いが変わらないよう、後者のほうが管理しやすいです。

クラシックのチェックアウトに入力欄を追加して保存する

ショートコード版のチェックアウトでは、woocommerce_checkout_fields フィルターで欄を追加し、woocommerce_checkout_create_order アクションで注文に保存するのが扱いやすい組み合わせです。

<?php
// チェックアウトの「注文メモ」グループに入力欄を追加
add_filter( 'woocommerce_checkout_fields', function ( $fields ) {
	$fields['order']['mytheme_delivery_note'] = array(
		'type'        => 'text',
		'label'       => '配送に関するご要望',
		'placeholder' => '例:午前中の配達を希望',
		'required'    => false,
		'class'       => array( 'form-row-wide' ),
		'maxlength'   => 100,
		'priority'    => 30,
	);
	return $fields;
} );

// サーバー側でも長さを検証(maxlength はブラウザ側の制限にすぎない)
add_action( 'woocommerce_after_checkout_validation', function ( $data, $errors ) {
	if ( ! empty( $data['mytheme_delivery_note'] ) && mb_strlen( $data['mytheme_delivery_note'] ) > 100 ) {
		$errors->add( 'validation', '配送に関するご要望は100文字以内で入力してください。' );
	}
}, 10, 2 );

// 注文の作成時に注文メタとして保存(HPOS でも従来方式でも動く書き方)
add_action( 'woocommerce_checkout_create_order', function ( $order, $data ) {
	if ( ! empty( $data['mytheme_delivery_note'] ) ) {
		$order->update_meta_data( '_mytheme_delivery_note', sanitize_text_field( $data['mytheme_delivery_note'] ) );
	}
}, 10, 2 );

ポイントは3つあります。まず、$_POST を直接読まずに $data 引数を使っていることです。woocommerce_checkout_fields に登録した欄は、WooCommerce が送信値を受け取る段階で型に応じたサニタイズを通し、required の指定があれば未入力チェックもしてくれます。チェックアウト送信時の nonce 検証も WooCommerce 本体が行います。それでも保存直前に sanitize_text_field() を重ねておくと、後から欄の型を変えたときの事故を防げます。

次に、woocommerce_checkout_create_order は注文がデータベースに保存される直前に呼ばれるので、ここでは update_meta_data() だけを呼び、save() は呼びません。WooCommerce がこの後でまとめて保存します。

最後に、メタキーの先頭に _(アンダースコア)を付けています。先頭が _ のキーは「保護されたメタ」として扱われ、管理画面の「カスタムフィールド」欄にそのまま出てこないため、運用担当者が誤って書き換える心配が減ります。キー名には他のプラグインと衝突しない接頭辞(ここでは mytheme_)を付けてください。

ブロックのチェックアウトでは追加フィールドAPIを使う

チェックアウトがブロックの場合は、WooCommerce が用意している追加チェックアウトフィールドの仕組み(Additional Checkout Fields API)を使います。WooCommerce 8.9 前後で安定版として提供され、その後も機能追加が続いています(2026年10月現在)。使える欄の型や配置場所の指定値はバージョンで変わることがあるため、導入するバージョンの開発者向けドキュメントで確認してから書いてください。

<?php
add_action( 'woocommerce_init', function () {
	if ( ! function_exists( 'woocommerce_register_additional_checkout_field' ) ) {
		return; // 古い WooCommerce では何もしない
	}
	woocommerce_register_additional_checkout_field(
		array(
			'id'       => 'mytheme/delivery-note', // 「名前空間/項目名」の形式
			'label'    => '配送に関するご要望',
			'location' => 'order',                 // 連絡先・住所・注文情報のいずれかの区画
			'type'     => 'text',
			'required' => false,
		)
	);
} );

この方法で登録した欄は、入力値の保存だけでなく、管理画面の注文編集画面・注文確認ページ・注文メールへの表示も WooCommerce 側が受け持ちます。表示のためのコードを自分で書かずに済むのが大きな利点です。一方で、値は WooCommerce が決めた接頭辞付きのメタキーで保存されるため、外部連携などで値を読む場合は、メタキーを推測して get_meta() するのではなく、ドキュメントに載っている取得方法を使うほうが将来の変更に強くなります。

クラシックとブロックの両方のコードを入れておくと、どちらのチェックアウトでも欄が出ますが、保存先のキーが異なる点に注意してください。運用中にチェックアウトを切り替える予定があるなら、どちらか一方にそろえるか、表示側で両方のキーを読むようにします。以降の表示・編集のコードはクラシック側で保存した _mytheme_delivery_note を前提にしています。

管理画面の注文詳細に表示し、編集できるようにする

管理画面の注文編集画面には、請求先・配送先の住所ブロックの下に処理を差し込むアクションがあります。表示だけなら次のように書きます。

<?php
add_action( 'woocommerce_admin_order_data_after_shipping_address', function ( $order ) {
	$note = $order->get_meta( '_mytheme_delivery_note' );
	if ( '' === $note ) {
		return;
	}
	echo '<p><strong>配送に関するご要望:</strong><br>' . esc_html( $note ) . '</p>';
} );

ショップ担当者が後から値を直せるようにしたい場合は、入力欄を出すアクションと、保存のアクションを組み合わせます。

<?php
// 注文情報ブロックに入力欄を出す
add_action( 'woocommerce_admin_order_data_after_order_details', function ( $order ) {
	woocommerce_wp_text_input(
		array(
			'id'            => 'mytheme_delivery_note',
			'label'         => '配送に関するご要望',
			'value'         => $order->get_meta( '_mytheme_delivery_note' ),
			'wrapper_class' => 'form-field-wide',
		)
	);
} );

// 注文の更新ボタンが押されたときに保存する
add_action( 'woocommerce_process_shop_order_meta', function ( $order_id ) {
	if ( ! current_user_can( 'edit_shop_orders' ) || ! isset( $_POST['mytheme_delivery_note'] ) ) {
		return;
	}
	$order = wc_get_order( $order_id );
	if ( ! $order ) {
		return;
	}
	$order->update_meta_data(
		'_mytheme_delivery_note',
		sanitize_text_field( wp_unslash( $_POST['mytheme_delivery_note'] ) )
	);
	$order->save();
}, 50 );

woocommerce_process_shop_order_meta は、WooCommerce が注文編集画面の nonce を検証した後に呼ばれます。それに加えて権限チェックを入れ、$_POST の値は wp_unslash() してから sanitize_text_field() を通しています。優先度を 50 にしているのは、WooCommerce 本体の保存処理より後に動かし、自分の変更が最後に書き込まれるようにするためです。保存後に注文一覧から開き直し、値が残っているかで動作を確認できます。

お客様側の注文詳細ページとメールに表示する

お客様が「マイアカウント」の注文履歴から開く注文詳細と、購入直後の注文完了ページには、どちらも注文テーブルを出すテンプレートが使われます。その直後に表示を足すには woocommerce_order_details_after_order_table を使います。

<?php
add_action( 'woocommerce_order_details_after_order_table', function ( $order ) {
	$note = $order->get_meta( '_mytheme_delivery_note' );
	if ( '' === $note ) {
		return;
	}
	echo '<section class="mytheme-order-extra"><h2>ご要望</h2><p>' . esc_html( $note ) . '</p></section>';
} );

// 注文メールに項目を追加(管理者宛て・お客様宛ての両方に出る)
add_filter( 'woocommerce_email_order_meta_fields', function ( $fields, $sent_to_admin, $order ) {
	$note = $order->get_meta( '_mytheme_delivery_note' );
	if ( '' !== $note ) {
		$fields['mytheme_delivery_note'] = array(
			'label' => '配送に関するご要望',
			'value' => $note,
		);
	}
	return $fields;
}, 10, 3 );

表示では esc_html() で出力をエスケープしています。保存時にサニタイズしていても、表示時のエスケープは別に必要です。データベースの値がほかの経路(インポートや別プラグイン)で書き換えられる可能性があるためです。メールに出したくない項目、たとえば社内向けのメモは、$sent_to_admin が true のときだけ追加するように分岐させます。

つまずきやすい点と確認方法

  • 入力欄が表示されない:ブロックのチェックアウトにクラシック用のフィルターを当てているケースが大半です。チェックアウトページの中身を確認してください。
  • 管理画面に値が出ない:HPOS 環境で get_post_meta() を使っていないか、メタキーの綴りと先頭の _ が保存側と表示側で一致しているかを見ます。
  • 一部の注文だけ値が無い:コード追加前の注文や、管理画面から手動作成した注文にはチェックアウトの保存処理が走りません。空のときは何も出さない作りにしておくと表示が崩れません。
  • キャッシュで古い表示が出る:チェックアウトやマイアカウントはページキャッシュの対象外にするのが原則です。キャッシュ系プラグインの除外設定を確認してください。

動作確認は、テスト用の決済方法(銀行振込や代金引換など、実際の課金が発生しないもの)で注文を1件作り、チェックアウト・注文完了ページ・マイアカウントの注文詳細・管理画面・届いたメールの5か所に値が出るかを順に見るのが手早い方法です。

まとめ

  • 先にチェックアウトがクラシックかブロックか、注文の保存方式が HPOS かを確認する。
  • クラシックは woocommerce_checkout_fields で欄を追加し、woocommerce_checkout_create_order で update_meta_data() により保存する。
  • ブロックのチェックアウトは追加チェックアウトフィールドAPIを使い、表示の多くを WooCommerce に任せる。
  • 注文の値の読み書きは get_post_meta() ではなく WC_Order のメソッドで行い、表示時は esc_html() でエスケープする。
  • 管理画面での編集を許す場合は権限チェックを入れ、ステージングでテスト注文を通してから本番に反映する。

お気軽にご相談ください

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