
🛠 Ekskluzywne taksonomie w WordPress: jak zastąpić checkboxy przyciskami radiowymi
Domyślnie WordPress pozwala przypisać do wpisu dowolną liczbę terminów jednej taksonomii. Checkboxy w sidebarze i gotowe, porządek. Ale co, jeśli klient potrzebuje, aby redaktor wybrał dokładnie jedną opcję? Na przykład „Typ projektu": case, landing page lub sklep internetowy. Trzy pozycje, a dwie z nich jednocześnie nie mają sensu.
Wbudowanego przełącznika „ustaw taksonomię jako wyłączną" w WordPressie nie ma. Zgłoszenie w Trac wisi od 2010 roku bez ruchu. Ale zadanie rozwiązuje się kodem, bez wtyczek i zbędnych zależności.
Pod koniec artykułu zamienisz standardowy metabox z checkboxami na panel z radio buttonami: dokładnie jeden termin na wpis, mniej błędów redaktora.
💡 Szybki przegląd:
- Rejestrujemy taksonomię z parametrem meta_box_cb, standardowy metabox nie pojawi się.
- Na hooku add_meta_boxes tworzymy własny blok z radio buttonami.
- Przez save_post zapisujemy wybrany termin w bazie danych.
- Pełny kod kopiuje się do functions.php motywu potomnego i działa od razu.
Czym są taksonomie i dlaczego potrzebna jest wyłączność
Taksonomia w WordPressie to mechanizm grupowania wpisów. Standardowe „Kategorie" i „Tagi" to również taksonomie. Niestandardową taksonomię tworzy się pod konkretne zadanie: „Typ projektu" dla portfolio, „Region" dla katalogu oddziałów, „Rodzaj usługi" dla cennika.
Wbudowane kategorie działają jak taksonomia wyłączna: wpis należy do dokładnie jednej kategorii, jeśli nie używa się wtyczek rozszerzających. Hierarchia „nadrzędna → podrzędna" podpowiada redaktorowi logikę wyboru.
Natomiast każda niestandardowa taksonomia zarejestrowana przez register_taxonomy() jest domyślnie niewyłączna. Interfejs: checkboxy lub pole z autouzupełnianiem. Dla tagów i cech produktu jest to poprawne. Ale kiedy każdy wpis musi mieć dokładnie jeden termin, checkboxy stają się źródłem błędów.
Rozwiązanie: ukryć standardowy metabox i wyrenderować własny, z radio buttonami i ścisłą kontrolą. To nie obejście, a udokumentowane API WordPressa, po prostu rozłożone na kilka kroków.
Krok 1: rejestrujemy niestandardową taksonomię
Podstawa: funkcja register_taxonomy(). Kod dodaje się w functions.php motywu potomnego lub przez wtyczkę Code Snippets. Stwórzmy taksonomię project_type dla standardowych wpisów:
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' );
Kluczowe parametry:
hierarchical => truewłącza strukturę drzewiastą, jak w kategoriach. Terminy budują hierarchię: „Design → Landing page", „Rozwój → Sklep internetowy".show_in_rest => trueudostępnia taksonomię w REST API i edytorze bloków. Bez tego Gutenberg nie zobaczy metaboxu.meta_box_cb => falseorazshow_in_quick_edit => falsecałkowicie usuwają standardowy interfejs wyboru terminów.
Po zapisaniu kodu taksonomia project_type pojawiła się w menu „Wpisy". Terminy dodaje się przez „Wpisy → Typ projektu", interfejs jest standardowy, jak dla kategorii.
Krok 2: ukrywamy standardowy metabox
W praktyce ten krok został już wykonany przez parametry meta_box_cb i show_in_quick_edit w kroku 1. Utwierdźmy się:
meta_box_cb => falseusuwa metabox ze strony edycji wpisu.show_in_quick_edit => falseukrywa taksonomię z panelu szybkiej i masowej edycji.
Bez nich WordPress dodaje domyślny interfejs: dla taksonomii hierarchicznych checkboxy kategorii, dla niehierarchicznych pole tagów z autouzupełnianiem.

Terminy wypełnia się przez osobną stronę zarządzania:

Krok 3: tworzymy niestandardowy metabox z radio buttonami
Rejestrujemy własny metabox przez hook add_meta_boxes. Dodaj w 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 }
Parametry add_meta_box():
project_type_box, wewnętrzne ID (dowolne, ale unikalne).'Тип проекта', tytuł w panelu administracyjnym.sd_render_project_type_meta_box, funkcja renderująca.'post', typ wpisu; można podać tablicę dla kilku CPT.'side', panel boczny. Alternatywy:'normal','advanced'.
Funkcja renderująca radio buttony:
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 }
Co jest tutaj ważne:
get_terms()zhide_empty => falsezwraca wszystkie terminy, włącznie z nieużywanymi.get_the_terms()zwraca tablicę, bierzemy pierwszy element[0], ponieważ logika gwarantuje nie więcej niż jeden termin.checked(), wbudowana funkcja WordPressa, wypisujechecked="checked"przy zgodności.esc_attr()orazesc_html(), obowiązkowe eskejpowanie.
Rezultat: czysty blok z radio buttonami:

Żadnych checkboxów, żadnej możliwości wyboru dwóch opcji.
Krok 4: zapisujemy termin przy zapisie wpisu
Bez tego kroku metabox jest czysto dekoracyjny. Używamy hooka 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 }
Szczegóły:
DOING_AUTOSAVE, pomijamy autozapis. Bez tego sprawdzenia termin jest nadpisywany w tle co 60 sekund.current_user_can( 'edit_post', $post_id ), podstawowa kontrola uprawnień.absint()konwertuje wartość na dodatnią liczbę całkowitą, bezpieczniej niż(int) sanitize_text_field().wp_set_object_terms()z pojedynczym ID (nie tablicą) zapisuje dokładnie jeden termin, usuwając poprzednie powiązania.
Gotowe. Zapisz wpis, radio button zostanie utrwalony. Przy następnym otwarciu wybrany termin jest podświetlony.
Pełny kod do skopiowania
Wszystkie cztery kroki w jednym fragmencie. Dodaj w functions.php motywu potomnego lub przez 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 }
⚠️ Przed wklejeniem wykonaj pełną kopię zapasową strony. Kod został sprawdzony na aktualnej wersji WordPressa z klasycznym edytorem. W Gutenbergu niestandardowy metabox wyświetla się w panelu bocznym bez zmian.
W praktyce wdrażaliśmy ten snippet na trzech projektach, wszędzie zadziałał bez poprawek. Jeśli masz hierarchiczny typ wpisu lub kilka ról redaktorów, zamień 'post' na swój slug i zweryfikuj uprawnienia w save_post.
W WordPress Trac od 2010 roku wisi zgłoszenie #14877 dotyczące natywnego wsparcia dla wyłącznych taksonomii. Pojawi się parametr 'exclusive' => true w jądrze, cała konstrukcja skurczy się do jednej linijki. Póki zgłoszenie jest bez ruchu, podejście z radio buttonami pozostaje podstawowym rozwiązaniem.
A poniżej krótkie wideo na ten temat, aby zobaczyć proces na własne oczy:
⁉️🤔 Często zadawane pytania
Czy to działa w Gutenbergu?
Tak. Parametr
show_in_rest => trueprzy rejestracji taksonomii włącza kompatybilność z edytorem bloków. Niestandardowy metabox pojawia się w bocznym panelu dokumentu, radio buttony wyświetlają się i zapisują poprawnie.
Czy można zastosować to podejście do dowolnych typów wpisów?
Tak. Zamień
'post'wregister_taxonomy()iadd_meta_box()na slug swojego CPT. Reszta kodu, nazwa taksonomii, etykieta, parametry, nie zmienia się. Podejście działa z każdym zarejestrowanym typem wpisu, włącznie z tymi utworzonymi przez ACF lub Custom Post Type UI.
Co się stanie, jeśli redaktor nie wybierze żadnego terminu?
Wpis zostanie zapisany bez przypisanego terminu,
save_postpo prostu nie wywoławp_set_object_terms(). Jeśli obowiązkowy wybór jest krytyczny, dodaj walidację JavaScript w panelu administracyjnym lub sprawdzenie przez hookpre_post_update.
Dlaczego nie użyć wtyczki Radio Buttons for Taxonomies?
Można. Wtyczka rozwiązuje zadanie bez kodu, 100 000+ aktywnych instalacji. Minus: kolejna zależność. Dla jednej-dwóch stron wtyczka jest uzasadniona. Dla agencji i multisite kod w motywie daje pełną kontrolę bez zbędnych aktualizacji i konfliktów.
Jak dodać wybór hierarchiczny (nadrzędny → podrzędny)?
Taksonomie hierarchiczne (
hierarchical => true) automatycznie otrzymują strukturę rodzic-dziecko. Aby wyświetlić hierarchię w niestandardowym metaboxie, zamieńget_terms()nawp_dropdown_categories()z parametrem'taxonomy' => 'project_type', funkcja wyświetli select z wcięciami.
Podsumowując: kiedy warto zawracać sobie głowę kodem
Wyłączna taksonomia nie jest potrzebna w każdym projekcie. Jeśli redaktorzy rozumieją logikę strony i nie mylą się przy wyborze, standardowe checkboxy wystarczą. Ale kiedy cena błędu jest wysoka (landing page w portfolio błędnie oznaczony zarówno jako „sklep internetowy", jak i „case"), pół godziny na kod zwraca się czystością treści.
- Jeśli masz jedną stronę i nie masz developera, zainstaluj Radio Buttons for Taxonomies. Działa bez kodu.
- Jeśli to agencja lub multisite, skopiuj powyższy kod do motywu bazowego. Mniej wtyczek, mniej punktów awarii przy aktualizacjach.
- Jeśli strona działa na czystym Gutenbergu, oceń, czy taksonomia jest w ogóle potrzebna. Być może wystarczy pole ACF z radio buttonami: mniej encji, szybszy panel administracyjny.
Zacznij od strony testowej. Zarejestruj taksonomię, dodaj trzy terminy i utwórz wpis, interfejs zadziała od razu. Napisz w komentarzach, do jakiego zadania użyłeś wyłącznej taksonomii.



