Skip to content

Все для WordPress, веб-розробки — і не тільки

🛠 Ексклюзивні таксономії в WordPress: як замінити чекбокси на радіокнопки

🛠 Ексклюзивні таксономії в 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 для стандартних записів:

1function 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}
15add_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 додає інтерфейс за замовчуванням: для ієрархічних таксономій, чекбокси категорій, для неієрархічних, поле тегів з автодоповненням.

Параметри meta_box_cb та show_in_quick_edit у register_taxonomy

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

Сторінка керування термінами кастомної таксономії в адмінці WordPress

Крок 3: створюємо кастомний метабокс із радіокнопками

Реєструємо власний метабокс через хук add_meta_boxes. Додайте у functions.php:

1add_action( 'add_meta_boxes', 'sd_add_project_type_meta_box' );
2
3function 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'.

Функція, що відмальовує радіокнопки:

1function 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:

1add_action( 'save_post', 'sd_save_project_type_term' );
2
3function 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 */
5function 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}
19add_action( 'init', 'sd_register_project_type_taxonomy' );
20
21add_action( 'add_meta_boxes', 'sd_add_project_type_meta_box' );
22
23function 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
34function 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
61add_action( 'save_post', 'sd_save_project_type_term' );
62
63function 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 з радіокнопками: менше сутностей, швидша адмінка.

Почніть із тестового сайту. Зареєструйте таксономію, додайте три терміни та створіть запис, інтерфейс запрацює одразу. Напишіть у коментарях, для якої задачі ви використали ексклюзивну таксономію.