カテゴリーのラジオボタン実装には、以前「Radio Buttons for Taxonomies」プラグインを使用していました。
このプラグインはブロックエディターにも対応しており、簡単にカテゴリー選択をラジオボタンにできるので重宝していました。
しかし、いくつかの課題がありました:
- プラグインの開発が2年以上更新されておらず、メンテナンス状況に懸念がありました。
- 実装後、ラジオボタンの選択肢に「カテゴリーなし」という不要なオプションが自動的に追加されてしまいました。
これらの問題を解決するため、カスタム実装への移行を決定しました。
新しい実装は、生成AI「Claude」との協力のもとで開発しました。
この方法では、functions.phpにコードをコピペしたあと、少しコードを変更するだけで、特定のカテゴリー選択をラジオボタンに変更することができます。
実装イメージ
投稿編集画面

ブロックエディターのサイドバーに、このような形で出ます。
投稿一覧の管理画面

投稿記事の一覧にある「クイック編集」にも対応させました。
(この対応に結構苦労しました…)
実装方法
functions.phpに以下のコードをコピペしてください。
(必ずテスト環境でテストしてから、本番に入れてくださいね。)
その際に、4行目あたりのworks_category
をタクソノミーのスラッグに変更してください。
<?php
// ラジオボタンにするカテゴリー選択
function change_interviews_category_to_radio($args, $post_type, $taxonomy) {
if ($taxonomy !== 'works_category') {//←ここに変更したいカスタムタクソノミーのスラッグを入れる
return $args;
}
// カスタムメタボックスコールバックとサニタイズ関数を設定
$args['meta_box_cb'] = 'interviews_category_radio_meta_box';
$args['meta_box_sanitize_cb'] = 'sanitize_interviews_category';
return $args;
}
add_filter('register_taxonomy_args', 'change_interviews_category_to_radio', 10, 3);
// カスタムメタボックスの表示
function interviews_category_radio_meta_box($post, $box) {
$defaults = array('taxonomy' => 'interviews_category');
if (!isset($box['args']) || !is_array($box['args']))
$args = array();
else
$args = $box['args'];
$args = wp_parse_args($args, $defaults);
$taxonomy = $args['taxonomy'];
$tax = get_taxonomy($taxonomy);
$selected = wp_get_object_terms($post->ID, $taxonomy, array('fields' => 'ids'));
$selected = (empty($selected) || is_wp_error($selected)) ? array() : $selected;
?>
<div id="taxonomy-<?php echo $taxonomy; ?>" class="categorydiv">
<ul id="<?php echo $taxonomy; ?>checklist" class="categorychecklist form-no-clear">
<?php
// カスタムウォーカーを使用してラジオボタンを表示
wp_terms_checklist($post->ID, array(
'taxonomy' => $taxonomy,
'selected_cats' => $selected,
'walker' => new Walker_Category_Radio(),
'checked_ontop' => false
));
?>
</ul>
<!-- 新しいカテゴリー追加フォーム -->
<div id="<?php echo $taxonomy; ?>-adder" class="wp-hidden-children">
<a id="<?php echo $taxonomy; ?>-add-toggle" href="#<?php echo $taxonomy; ?>-add" class="hide-if-no-js">
+ <?php echo $tax->labels->add_new_item; ?>
</a>
<p id="<?php echo $taxonomy; ?>-add" class="category-add wp-hidden-child">
<label class="screen-reader-text" for="new<?php echo $taxonomy; ?>"><?php echo $tax->labels->add_new_item; ?></label>
<input type="text" name="new<?php echo $taxonomy; ?>" id="new<?php echo $taxonomy; ?>" class="form-required form-input-tip" value="<?php echo esc_attr($tax->labels->new_item_name); ?>" aria-required="true"/>
<input type="button" id="<?php echo $taxonomy; ?>-add-submit" class="button category-add-submit" value="<?php echo esc_attr($tax->labels->add_new_item); ?>" />
<?php wp_nonce_field('add-' . $taxonomy, '_ajax_nonce-add-' . $taxonomy, false); ?>
<span id="<?php echo $taxonomy; ?>-ajax-response"></span>
</p>
</div>
</div>
<?php
}
// カテゴリー選択のサニタイズ
function sanitize_interviews_category($term_ids) {
if (!is_array($term_ids)) {
return array(absint($term_ids));
}
return array_map('absint', $term_ids);
}
// カスタムウォーカークラス:チェックボックスをラジオボタンに変更
class Walker_Category_Radio extends Walker {
public function start_el(&$output, $category, $depth = 0, $args = array(), $id = 0) {
$taxonomy = $args['taxonomy'];
$name = 'tax_input[' . $taxonomy . '][]';
$args['popular_cats'] = empty($args['popular_cats']) ? array() : $args['popular_cats'];
$class = in_array($category->term_id, $args['popular_cats']) ? ' class="popular-category"' : '';
$args['selected_cats'] = empty($args['selected_cats']) ? array() : $args['selected_cats'];
$checked = in_array($category->term_id, $args['selected_cats']) ? ' checked="checked"' : '';
$output .= "\n<li id='{$taxonomy}-{$category->term_id}'$class>" .
'<label class="selectit"><input value="' . $category->term_id . '" type="radio" name="' . $name . '" id="in-'.$taxonomy.'-' . $category->term_id . '"' . $checked . '/> ' .
esc_html(apply_filters('the_category', $category->name)) . '</label>';
}
}
// Gutenbergエディタ用のラジオボタン実装
function interviews_category_radio_for_gutenberg() {
$script = "
( function( wp ) {
var el = wp.element.createElement;
var RadioControl = wp.components.RadioControl;
var __ = wp.i18n.__;
var useSelect = wp.data.useSelect;
var useDispatch = wp.data.useDispatch;
wp.hooks.addFilter(
'editor.PostTaxonomyType',
'my-plugin/radio-control-interviews-category',
function( OriginalComponent ) {
return function( props ) {
if ( props.slug !== 'interviews_category' ) {
return el( OriginalComponent, props );
}
var terms = useSelect( function( select ) {
return select( 'core' ).getEntityRecords( 'taxonomy', 'interviews_category', { per_page: -1 } );
}, [] );
var selectedTerms = useSelect( function( select ) {
return select( 'core/editor' ).getEditedPostAttribute( 'interviews_category' );
}, [] );
var { editPost } = useDispatch( 'core/editor' );
if ( !terms ) {
return 'Loading...';
}
var options = terms.map( function( term ) {
return { label: term.name, value: term.id.toString() };
});
return el( RadioControl, {
label: __( 'Interviews Category', 'text-domain' ),
selected: selectedTerms && selectedTerms.length ? selectedTerms[0].toString() : '',
options: options,
onChange: function( newTerm ) {
editPost( { interviews_category: [ parseInt(newTerm) ] } );
}
} );
};
}
);
} )( window.wp );
";
wp_add_inline_script( 'wp-edit-post', $script );
}
add_action( 'enqueue_block_editor_assets', 'interviews_category_radio_for_gutenberg' );
// カテゴリーの保存処理
function save_interviews_category($post_id) {
// 自動保存、権限チェック
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (!current_user_can('edit_post', $post_id)) return;
// post type チェック
if (get_post_type($post_id) != 'interviews') return;
// カテゴリーの保存
if (isset($_POST['tax_input']['interviews_category']) && is_array($_POST['tax_input']['interviews_category'])) {
$term_id = (int) $_POST['tax_input']['interviews_category'][0];
wp_set_object_terms($post_id, $term_id, 'interviews_category');
}
}
// クイック編集用のカスタムボックス(内容は空)
function custom_quick_edit_interviews_category($column_name, $post_type) {
if ($column_name != 'taxonomy-interviews_category' || $post_type != 'interviews') return;
// この関数の中身は空にします
}
add_action('quick_edit_custom_box', 'custom_quick_edit_interviews_category', 10, 2);
// チェックボックスをラジオボタンに変換するJavaScript
function convert_interviews_category_to_radio() {
global $post_type;
if ($post_type !== 'interviews') return;
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// チェックボックスをラジオボタンに変換する関数
function convertToRadio() {
$('.interviews_category-checklist, #interviews_category-checklist').each(function() {
var $checklist = $(this);
$checklist.find('input[type="checkbox"]').each(function() {
if ($(this).attr('type') !== 'radio') {
$(this).attr('type', 'radio');
// name属性を変更
$(this).attr('name', 'tax_input[interviews_category][]');
}
});
});
}
// 初期ロード時と動的に追加された要素に対して実行
convertToRadio();
$(document).ajaxComplete(function() {
convertToRadio();
});
// 新しいカテゴリー追加処理
$('#interviews_category-add-submit').on('click', function(e) {
e.preventDefault();
var newCat = $('#newinterviews_category').val();
var nonce = $('#_ajax_nonce-add-interviews_category').val();
$.post(ajaxurl, {
action: 'add-interviews_category',
newinterviews_category: newCat,
_ajax_nonce: nonce,
taxonomy: 'interviews_category'
}, function(response) {
if (response && response.success) {
// 新しいカテゴリーをリストに追加
var newTerm = response.data;
var newItem = '<li id="interviews_category-' + newTerm.term_id + '">' +
'<label class="selectit">' +
'<input value="' + newTerm.term_id + '" type="radio" name="tax_input[interviews_category][]" id="in-interviews_category-' + newTerm.term_id + '"> ' +
newTerm.name +
'</label></li>';
$('#interviews_categorychecklist').append(newItem);
// 入力フィールドをクリア
$('#newinterviews_category').val('');
// ラジオボタンに変換
convertToRadio();
}
});
});
// クイック編集用の処理
var $wp_inline_edit = inlineEditPost.edit;
inlineEditPost.edit = function(id) {
$wp_inline_edit.apply(this, arguments);
var post_id = 0;
if (typeof(id) == 'object') {
post_id = parseInt(this.getId(id));
}
if (post_id > 0) {
var edit_row = $('#edit-' + post_id);
var post_row = $('#post-' + post_id);
// カテゴリー情報を取得
var categoryText = $('.column-taxonomy-interviews_category', post_row).text().trim();
// ラジオボタンに変換
convertToRadio();
// 重複したチェックリストを削除
edit_row.find('.cat-checklist.interviews_category-checklist').not(':first').remove();
// 各カテゴリーに対してチェックを入れる
edit_row.find('input[name="tax_input[interviews_category][]"]').each(function() {
var label = $(this).parent().text().trim();
if (label === categoryText) {
$(this).prop('checked', true);
} else {
$(this).prop('checked', false);
}
});
}
};
// 既存の投稿一覧ページのチェックボックスをラジオボタンに変換
$('.widefat.posts').on('click', '.editinline', function() {
setTimeout(function() {
convertToRadio();
}, 200);
});
});
</script>
<?php
}
add_action('admin_footer-post.php', 'convert_interviews_category_to_radio');
add_action('admin_footer-post-new.php', 'convert_interviews_category_to_radio');
add_action('admin_footer-edit.php', 'convert_interviews_category_to_radio');
注意点
- プラグイン「Radio Buttons for Taxonomies」は削除してから、このコードを入れてください。
サーバーエラー(500エラー)が発生して、サイトが閲覧できなくなります。 - 「新しいカテゴリーを作成する時、なかなかページが更新されない」です。
その時は「ショートカットキーなどで、ページ更新」をしてください。
すると、新しいカテゴリーが作成されています。