
🛠 Ексклюзивні таксономії в WordPress: як замінити чекбокси на радіокнопки
За замовчуванням WordPress дозволяє прив’язати до запису скільки завгодно термінів однієї таксономії. Чекбокси в сайдбарі, і все, порядок. Але що якщо замовнику потрібно, щоб редактор вибрав рівно один варіант? Наприклад, «Тип проєкту», кейс, лендинг або інтернет-магазин. Три позиції, і дві з них разом не мають сенсу.
Штатного перемикача «зробити таксономію ексклюзивною» у WordPress немає. Заявка в Trac висить з 2010 року без руху. Але задача вирішується кодом, без плагінів і зайвих залежностей.
Наприкінці статті ви перетворите стандартний метабокс із чекбоксами на панель із радіокнопками: рівно один термін на запис, менше помилок редактора.
💡 Швидкий огляд:
- Реєструємо таксономію з параметром meta_box_cb, стандартний метабокс не з’явиться.
- На хуку add_meta_boxes створюємо власний блок із радіокнопками.
- Через save_post записуємо вибраний термін у базу.
- Повний код копіюється у functions.php дочірньої теми та працює одразу.
Що таке таксономії та навіщо потрібна ексклюзивність
Таксономія у WordPress — це механізм групування записів. Стандартні «Категорії» та «Мітки», теж таксономії. Кастомна таксономія створюється під конкретну задачу: «Тип проєкту» для портфоліо, «Регіон» для каталогу філій, «Вид послуги» для прайс-листа.
Вбудовані категорії працюють як ексклюзивна таксономія: запис належить рівно одній категорії, якщо не використовувати плагіни-розширення. Ієрархія «батьківська → дочірня» підказує редактору логіку вибору.
А от будь-яка зареєстрована через register_taxonomy() кастомна таксономія за замовчуванням неексклюзивна. Інтерфейс, чекбокси або поле з автодоповненням. Для тегів і характеристик товару це правильно. Але коли кожен запис зобов’язаний мати рівно один термін, чекбокси стають джерелом помилок.
Рішення: приховати стандартний метабокс і відмалювати свій, із радіокнопками та строгим контролем. Це не обхідний шлях, а документований API WordPress, просто розтягнутий на кілька кроків.
Крок 1: реєструємо кастомну таксономію
База, функція register_taxonomy(). Код додається у functions.php дочірньої теми або через плагін Code Snippets. Створимо таксономію project_type для стандартних записів:
1 function sd_register_project_type_taxonomy() { 2 register_taxonomy( 3 'project_type', 4 'post', 5 array( 6 'label' => __( 'Тип проекта', 'textdomain' ), 7 'public' => true, 8 'show_in_rest' => true, 9 'hierarchical' => true, 10 'show_in_quick_edit' => false, 11 'meta_box_cb' => false, 12 ) 13 ); 14 } 15 add_action( 'init', 'sd_register_project_type_taxonomy' );
Ключові параметри:
hierarchical => trueвмикає деревоподібну структуру, як у категорій. Терміни вибудовуються в ієрархію: «Дизайн → Лендинг», «Розробка → Інтернет-магазин».show_in_rest => trueробить таксономію доступною в REST API та редакторі блоків. Без цього Gutenberg не побачить метабокс.meta_box_cb => falseтаshow_in_quick_edit => falseповністю прибирають стандартний інтерфейс вибору термінів.
Після збереження коду таксономія project_type з’явилася в меню «Записи». Терміни додаються через «Записи → Тип проєкту», інтерфейс стандартний, як для рубрик.
Крок 2: приховуємо стандартний метабокс
Фактично цей крок уже зроблено параметрами meta_box_cb та show_in_quick_edit на кроці 1. Закріпимо:
meta_box_cb => falseприбирає метабокс зі сторінки редагування запису.show_in_quick_edit => falseховає таксономію з панелі швидкого та масового редагування.
Без них WordPress додає інтерфейс за замовчуванням: для ієрархічних таксономій, чекбокси категорій, для неієрархічних, поле тегів з автодоповненням.

Терміни заповнюються через окрему сторінку керування:

Крок 3: створюємо кастомний метабокс із радіокнопками
Реєструємо власний метабокс через хук add_meta_boxes. Додайте у functions.php:
1 add_action( 'add_meta_boxes', 'sd_add_project_type_meta_box' ); 2 3 function sd_add_project_type_meta_box() { 4 add_meta_box( 5 'project_type_box', 6 __( 'Тип проекта', 'textdomain' ), 7 'sd_render_project_type_meta_box', 8 'post', 9 'side', 10 'default' 11 ); 12 }
Параметри add_meta_box():
project_type_box, внутрішній ID (довільний, але унікальний).'Тип проекта', заголовок в адмінці.sd_render_project_type_meta_box, функція відмальовування.'post', тип запису; можна масив для кількох CPT.'side', бічна панель. Альтернативи:'normal','advanced'.
Функція, що відмальовує радіокнопки:
1 function sd_render_project_type_meta_box( $post ) { 2 $terms = get_terms( array( 3 'taxonomy' => 'project_type', 4 'hide_empty' => false, 5 ) ); 6 7 if ( empty( $terms ) || is_wp_error( $terms ) ) { 8 echo '<p>Сначала добавьте термины на странице «Тип проекта».</p>'; 9 return; 10 } 11 12 $current_terms = get_the_terms( $post->ID, 'project_type' ); 13 $current_id = ( ! empty( $current_terms ) && ! is_wp_error( $current_terms ) ) 14 ? $current_terms[0]->term_id 15 : 0; 16 17 foreach ( $terms as $term ) : ?> 18 <label style="display:block;margin-bottom:4px;"> 19 <input type="radio" 20 name="project_type_term" 21 value="<?php echo esc_attr( $term->term_id ); ?>" 22 <?php checked( $current_id, $term->term_id ); ?>> 23 <?php echo esc_html( $term->name ); ?> 24 </label> 25 <?php endforeach; 26 }
Що тут важливо:
get_terms()зhide_empty => falseповертає всі терміни, включно з невикористаними.get_the_terms()повертає масив, беремо перший елемент[0], оскільки логіка гарантує не більше одного терміна.checked(), вбудована функція WordPress, виводитьchecked="checked"при збігу.esc_attr()таesc_html(), обов’язкове екранування.
Результат, чистий блок із радіокнопками:

Жодних чекбоксів, жодної можливості вибрати два варіанти.
Крок 4: зберігаємо термін при збереженні запису
Без цього кроку метабокс суто декоративний. Використовуємо хук save_post:
1 add_action( 'save_post', 'sd_save_project_type_term' ); 2 3 function sd_save_project_type_term( $post_id ) { 4 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 5 return; 6 } 7 8 if ( ! current_user_can( 'edit_post', $post_id ) ) { 9 return; 10 } 11 12 if ( isset( $_POST['project_type_term'] ) ) { 13 $term_id = absint( $_POST['project_type_term'] ); 14 wp_set_object_terms( $post_id, $term_id, 'project_type' ); 15 } 16 }
Деталі:
DOING_AUTOSAVE, пропускаємо автозбереження. Без цієї перевірки термін перезаписується у фоновому режимі кожні 60 секунд.current_user_can( 'edit_post', $post_id ), базовий контроль прав.absint()приводить значення до додатного цілого, безпечніше ніж(int) sanitize_text_field().wp_set_object_terms()з одиничним ID (не масивом) записує рівно один термін, видаляючи попередні зв’язки.
Готово. Збережіть запис, радіокнопка зафіксується. При наступному відкритті вибраний термін підсвічений.
Повний код для копіювання
Усі чотири кроки в одному фрагменті. Додайте у functions.php дочірньої теми або через Code Snippets:
1 /** 2 * Эксклюзивная таксономия «Тип проекта» — один термин на пост. 3 * Добавить в functions.php дочерней темы. 4 */ 5 function sd_register_project_type_taxonomy() { 6 register_taxonomy( 7 'project_type', 8 'post', 9 array( 10 'label' => __( 'Тип проекта', 'textdomain' ), 11 'public' => true, 12 'show_in_rest' => true, 13 'hierarchical' => true, 14 'show_in_quick_edit' => false, 15 'meta_box_cb' => false, 16 ) 17 ); 18 } 19 add_action( 'init', 'sd_register_project_type_taxonomy' ); 20 21 add_action( 'add_meta_boxes', 'sd_add_project_type_meta_box' ); 22 23 function sd_add_project_type_meta_box() { 24 add_meta_box( 25 'project_type_box', 26 __( 'Тип проекта', 'textdomain' ), 27 'sd_render_project_type_meta_box', 28 'post', 29 'side', 30 'default' 31 ); 32 } 33 34 function sd_render_project_type_meta_box( $post ) { 35 $terms = get_terms( array( 36 'taxonomy' => 'project_type', 37 'hide_empty' => false, 38 ) ); 39 40 if ( empty( $terms ) || is_wp_error( $terms ) ) { 41 echo '<p>Сначала добавьте термины на странице «Тип проекта».</p>'; 42 return; 43 } 44 45 $current_terms = get_the_terms( $post->ID, 'project_type' ); 46 $current_id = ( ! empty( $current_terms ) && ! is_wp_error( $current_terms ) ) 47 ? $current_terms[0]->term_id 48 : 0; 49 50 foreach ( $terms as $term ) : ?> 51 <label style="display:block;margin-bottom:4px;"> 52 <input type="radio" 53 name="project_type_term" 54 value="<?php echo esc_attr( $term->term_id ); ?>" 55 <?php checked( $current_id, $term->term_id ); ?>> 56 <?php echo esc_html( $term->name ); ?> 57 </label> 58 <?php endforeach; 59 } 60 61 add_action( 'save_post', 'sd_save_project_type_term' ); 62 63 function sd_save_project_type_term( $post_id ) { 64 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 65 return; 66 } 67 68 if ( ! current_user_can( 'edit_post', $post_id ) ) { 69 return; 70 } 71 72 if ( isset( $_POST['project_type_term'] ) ) { 73 $term_id = absint( $_POST['project_type_term'] ); 74 wp_set_object_terms( $post_id, $term_id, 'project_type' ); 75 } 76 }
⚠️ Перед вставленням зробіть повний бекап сайту. Код перевірено на актуальній версії WordPress із класичним редактором. У Gutenberg кастомний метабокс відображається в бічній панелі без змін.
На практиці ми ставили цей снипет на три проєкти, скрізь злетіло без правок. Якщо у вас ієрархічний тип запису або кілька ролей редакторів, замініть 'post' на свій slug і звірте права в save_post.
У WordPress Trac з 2010 року висить заявка #14877 на нативну підтримку ексклюзивних таксономій. З’явиться параметр 'exclusive' => true в ядрі, уся конструкція стиснеться до одного рядка. Поки заявка без руху, підхід із радіокнопками залишається основним.
А нижче, коротке відео за темою, щоб побачити процес на власні очі:
⁉️🤔 Часті запитання
Чи працює це в Gutenberg?
Так. Параметр
show_in_rest => trueпри реєстрації таксономії вмикає сумісність із редактором блоків. Кастомний метабокс з’являється в бічній панелі документа, радіокнопки відображаються та зберігаються коректно.
Чи можна застосувати підхід до довільних типів записів?
Так. Замініть
'post'уregister_taxonomy()таadd_meta_box()на slug вашого CPT. Решта коду, назва таксономії, label, параметри, не змінюється. Підхід працює з будь-яким зареєстрованим типом запису, включно зі створеними через ACF або Custom Post Type UI.
Що станеться, якщо редактор не вибере жоден термін?
Запис збережеться без прив’язаного терміна,
save_postпросто не викличеwp_set_object_terms(). Якщо обов’язковий вибір критичний, додайте JavaScript-валідацію в адмінці або перевірку через хукpre_post_update.
Чому не використати плагін Radio Buttons for Taxonomies?
Можна. Плагін вирішує задачу без коду, 100 000+ активних встановлень. Мінус, ще одна залежність. Для одного-двох сайтів плагін виправданий. Для агенцій і мультисайтів код у темі дає повний контроль без зайвих оновлень і конфліктів.
Як додати ієрархічний вибір (батьківська → дочірня)?
Ієрархічні таксономії (
hierarchical => true) отримують структуру батько-нащадок автоматично. Щоб відобразити ієрархію в кастомному метабоксі, замінітьget_terms()наwp_dropdown_categories()з параметром'taxonomy' => 'project_type', функція виведе select із відступами.
Що в підсумку: коли варто заморочуватися з кодом
Ексклюзивна таксономія потрібна не кожному проєкту. Якщо редактори розуміють логіку сайту і не помиляються при виборі, стандартних чекбоксів достатньо. Але коли ціна помилки висока (лендинг у портфоліо помилково позначений і як «інтернет-магазин», і як «кейс»), пів години на код окупаються чистотою контенту.
- Якщо у вас один сайт і немає розробника, поставте Radio Buttons for Taxonomies. Працює без коду.
- Якщо агенція або мультисайт, скопіюйте код вище в базову тему. Менше плагінів, менше точок відмови при оновленнях.
- Якщо сайт на чистому Gutenberg, оцініть, чи потрібна таксономія взагалі. Можливо, вистачить поля ACF з радіокнопками: менше сутностей, швидша адмінка.
Почніть із тестового сайту. Зареєструйте таксономію, додайте три терміни та створіть запис, інтерфейс запрацює одразу. Напишіть у коментарях, для якої задачі ви використали ексклюзивну таксономію.



