2026年6月22日

2026年6月22日

WordPressのタクソノミーにカスタムメタフィールドを追加する実装方法(ターム メタ)

カテゴリーやカスタムタクソノミーの各タームに「テーマカラー」「リード文」のような独自の項目を持たせるには、WordPress 4.4 で導入されたターム メタ(wp_termmeta テーブル)を使います。実装は「register_term_meta() で項目を登録する」「追加・編集画面に入力欄を出す」「nonce と権限を確認して保存する」「フロントで取得してエスケープして表示する」の四段階です。この記事ではカテゴリーを例に、そのまま動くコードと各手順の注意点を説明します。

ターム メタの仕組みと使いどころ

投稿に投稿メタ(カスタムフィールド)があるように、タームにもキーと値の組を保存できます。操作する関数は投稿メタとよく似ています。

  • add_term_meta() / update_term_meta() / delete_term_meta():保存・更新・削除
  • get_term_meta( $term_id, $key, true ):一件の値を取得
  • register_term_meta( $taxonomy, $key, $args ):型やサニタイズ方法、REST での公開を登録

使いどころは、カテゴリーごとに色やアイコンを変えたい、タグのアーカイブページ冒頭に説明文と画像を出したい、地域タクソノミーに緯度経度を持たせたい、といった場面です。タームの「説明」欄は一つしかないため、複数の項目を持たせたいときにターム メタが役立ちます。

以前はタームごとにオプション(wp_options)へ保存する方法が紹介されていましたが、現在はターム メタを使うのが標準的です。古い記事のコードを流用する場合は、保存先がどちらになっているかを確認してください。

手順1:メタ項目を登録する

最初に、保存する項目の型とサニタイズ方法を登録します。コードは子テーマの functions.php か mu-plugin に置きます。タクソノミーの見た目に関する項目でもデータは残り続けるため、テーマ変更後も使うならプラグイン側が適しています。作業前にはバックアップを取り、ステージングで試してください。

<?php
add_action( 'init', 'myprefix_register_category_meta' );

function myprefix_register_category_meta() {
	register_term_meta(
		'category',
		'myprefix_color',
		array(
			'type'              => 'string',
			'single'            => true,
			'sanitize_callback' => 'sanitize_hex_color',
			'show_in_rest'      => true,
		)
	);

	register_term_meta(
		'category',
		'myprefix_lead',
		array(
			'type'              => 'string',
			'single'            => true,
			'sanitize_callback' => 'sanitize_textarea_field',
			'show_in_rest'      => true,
		)
	);
}

登録しておくと、update_term_meta() で保存するときにも sanitize_callback が通るため、保存処理のどこかで漏れがあっても値が整えられます。show_in_rest を true にすると、REST API のターム情報に meta として含まれます。外部に出したくない値なら false のままにしてください。

手順2:追加画面と編集画面に入力欄を出す

管理画面のターム追加フォームと編集フォームは HTML の構造が違うため、フックも二つに分かれています。

  • {$taxonomy}_add_form_fields:一覧画面の左側にある「新規追加」フォーム。div.form-field で囲みます。
  • {$taxonomy}_edit_form_fields:個別の編集画面。表の行(tr.form-field)で出力します。
<?php
// 新規追加フォーム
add_action( 'category_add_form_fields', 'myprefix_category_add_fields' );

function myprefix_category_add_fields( $taxonomy ) {
	wp_nonce_field( 'myprefix_save_term_meta', 'myprefix_term_meta_nonce', false );
	?>
	<div class="form-field">
		<label for="myprefix-color">テーマカラー</label>
		<input type="text" name="myprefix_color" id="myprefix-color" value="" placeholder="#336699">
		<p class="description">#から始まる16進数で入力します。</p>
	</div>
	<div class="form-field">
		<label for="myprefix-lead">リード文</label>
		<textarea name="myprefix_lead" id="myprefix-lead" rows="3"></textarea>
	</div>
	<?php
}

// 編集フォーム
add_action( 'category_edit_form_fields', 'myprefix_category_edit_fields', 10, 2 );

function myprefix_category_edit_fields( $term, $taxonomy ) {
	$color = get_term_meta( $term->term_id, 'myprefix_color', true );
	$lead  = get_term_meta( $term->term_id, 'myprefix_lead', true );
	wp_nonce_field( 'myprefix_save_term_meta', 'myprefix_term_meta_nonce', false );
	?>
	<tr class="form-field">
		<th scope="row"><label for="myprefix-color">テーマカラー</label></th>
		<td>
			<input type="text" name="myprefix_color" id="myprefix-color" value="<?php echo esc_attr( $color ); ?>">
			<p class="description">#から始まる16進数で入力します。</p>
		</td>
	</tr>
	<tr class="form-field">
		<th scope="row"><label for="myprefix-lead">リード文</label></th>
		<td><textarea name="myprefix_lead" id="myprefix-lead" rows="3"><?php echo esc_textarea( $lead ); ?></textarea></td>
	</tr>
	<?php
}

既存の値を入力欄に戻すときも、esc_attr() や esc_textarea() でエスケープします。管理画面だからと省くと、悪意ある値が保存されたときに画面が崩れたりスクリプトが動いたりする原因になります。

wp_nonce_field() の第3引数を false にしているのは、フォームに元からある参照元のフィールドと重複させないためです。新規追加フォームは画面遷移せずに Ajax で送信されますが、フォーム内の入力欄はまとめて送られるので、ここで出力した nonce も一緒に届きます。

手順3:nonce と権限を確認して保存する

保存には、新規作成後に呼ばれる created_{$taxonomy} と、更新後に呼ばれる edited_{$taxonomy} を使います。どちらも第1引数がタームIDです。

<?php
add_action( 'created_category', 'myprefix_save_category_meta', 10, 2 );
add_action( 'edited_category', 'myprefix_save_category_meta', 10, 2 );

function myprefix_save_category_meta( $term_id, $tt_id ) {
	if ( ! isset( $_POST['myprefix_term_meta_nonce'] ) ) {
		return;
	}
	if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['myprefix_term_meta_nonce'] ) ), 'myprefix_save_term_meta' ) ) {
		return;
	}
	if ( ! current_user_can( 'edit_term', $term_id ) ) {
		return;
	}

	if ( isset( $_POST['myprefix_color'] ) ) {
		$color = sanitize_hex_color( wp_unslash( $_POST['myprefix_color'] ) );
		if ( $color ) {
			update_term_meta( $term_id, 'myprefix_color', $color );
		} else {
			delete_term_meta( $term_id, 'myprefix_color' );
		}
	}

	if ( isset( $_POST['myprefix_lead'] ) ) {
		$lead = sanitize_textarea_field( wp_unslash( $_POST['myprefix_lead'] ) );
		update_term_meta( $term_id, 'myprefix_lead', $lead );
	}
}

確認していることは三つです。nonce で「この画面から送られたリクエストか」、edit_term で「このユーザーがこのタームを編集できるか」、サニタイズで「値が期待した形か」。wp_unslash() は、WordPress が $_POST に付けるスラッシュを外すために入れています。

nonce を確認せずに isset( $_POST[...] ) だけで保存すると、WP-CLI やインポート処理など別の経路でタームが作られたときに意図しない値が書き込まれることがあります。nonce がないリクエストでは何もしない、という書き方にしておくと安全です。

手順4:一覧画面に列を追加する(任意)

設定した値を一覧で見渡せると、入力漏れに気づきやすくなります。

<?php
add_filter( 'manage_edit-category_columns', 'myprefix_category_columns' );

function myprefix_category_columns( $columns ) {
	$columns['myprefix_color'] = 'カラー';
	return $columns;
}

add_filter( 'manage_category_custom_column', 'myprefix_category_column_content', 10, 3 );

function myprefix_category_column_content( $content, $column_name, $term_id ) {
	if ( 'myprefix_color' !== $column_name ) {
		return $content;
	}
	$color = sanitize_hex_color( get_term_meta( $term_id, 'myprefix_color', true ) );
	if ( ! $color ) {
		return '—';
	}
	return sprintf(
		'<span style="display:inline-block;width:1em;height:1em;background:%1$s;vertical-align:middle;"></span> %1$s',
		esc_attr( $color )
	);
}

manage_{$taxonomy}_custom_column はアクションではなくフィルターなので、echo せずに文字列を返します。

手順5:フロントエンドで表示する

カテゴリーアーカイブのテンプレート(category.php や archive.php)では、get_queried_object() で表示中のタームを取得できます。

<?php
$term = get_queried_object();

if ( $term instanceof WP_Term ) {
	$color = sanitize_hex_color( get_term_meta( $term->term_id, 'myprefix_color', true ) );
	$lead  = get_term_meta( $term->term_id, 'myprefix_lead', true );
	?>
	<header class="term-header"<?php echo $color ? ' style="border-left:6px solid ' . esc_attr( $color ) . ';"' : ''; ?>>
		<h1 class="term-title"><?php echo esc_html( single_term_title( '', false ) ); ?></h1>
		<?php if ( $lead ) : ?>
			<p class="term-lead"><?php echo nl2br( esc_html( $lead ) ); ?></p>
		<?php endif; ?>
	</header>
	<?php
}

ブロックテーマでは PHP テンプレートを直接編集しないことが多いため、同じ処理をショートコードや独自ブロックに切り出して、サイトエディターのテンプレートに配置する形になります。

メタの値でタームを絞り込みたい場合は、get_terms() に meta_key や meta_query を渡せます。ただしターム数が多いサイトではクエリが重くなりやすいので、表示のたびに実行するのではなく結果をキャッシュすることも検討してください。

プラグインで済ませる選択肢

コードを書かずに済ませたい場合は、Advanced Custom Fields(ACF)のフィールドグループを、表示条件にタクソノミーを指定して作る方法があります。タームへのフィールド追加は無料版でも使える機能ですが、使えるフィールドの種類や画面の表記は版や更新で変わることがあります(2026年10月現在)。ACF で保存した値もターム メタとして保存されるため、取得には get_field( 'field_name', $term ) のほか get_term_meta() も使えます。

まとめ

  • タームに独自の項目を持たせるにはターム メタを使い、register_term_meta() で型とサニタイズを登録しておく。
  • 入力欄は {$taxonomy}_add_form_fields と {$taxonomy}_edit_form_fields の二か所に出し、HTML構造の違いに合わせる。
  • 保存は created_{$taxonomy} と edited_{$taxonomy} で、nonce・edit_term 権限・サニタイズの三点を確認してから行う。
  • 表示時は esc_html() や esc_attr() でエスケープする。
  • コードを書かない場合は ACF のタクソノミー向けフィールドグループも選択肢になる。

お気軽にご相談ください

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