Cum să afișezi o listă ierarhică de termeni?
Am o taxonomie ierarhică numită 'locații geografice'. Aceasta conține continente pe primul nivel, și apoi țările pentru fiecare continent. Exemplu:
Europa
- Irlanda
- Spania
- Suedia
Asia
- Laos
- Thailanda
- Vietnam
etc.
Folosind get_terms() am reușit să afișez lista completă de termeni, dar continentele se amestecă cu țările, într-o singură listă plată.
Cum pot afișa o listă ierarhică precum cea de mai sus?
Înțeleg că aceasta este o întrebare foarte veche, dar dacă aveți nevoie să construiți o structură reală de termeni, această metodă vă poate fi utilă:
/**
* Sortează recursiv un array de termeni ai taxonomiei ierarhic. Categoriile copil vor fi
* plasate într-un membru 'children' al termenului lor părinte.
* @param Array $cats obiecte ale termenilor taxonomiei de sortat
* @param Array $into array-ul rezultat în care vor fi plasați
* @param integer $parentId ID-ul părintelui curent în care vor fi plasați
*/
function sorteaza_termenii_ierarhic(Array &$cats, Array &$into, $parentId = 0)
{
foreach ($cats as $i => $cat) {
if ($cat->parent == $parentId) {
$into[$cat->term_id] = $cat;
unset($cats[$i]);
}
}
foreach ($into as $topCat) {
$topCat->children = array();
sorteaza_termenii_ierarhic($cats, $topCat->children, $topCat->term_id);
}
}
Utilizarea este următoarea:
$categories = get_terms('my_taxonomy_name', array('hide_empty' => false));
$categoryHierarchy = array();
sorteaza_termenii_ierarhic($categories, $categoryHierarchy);
var_dump($categoryHierarchy);

Acesta este de fapt foarte bun. Aș schimba un singur lucru: $into[$cat->term_id] = $cat;
în $into[] = $cat;
Folosirea ID-ului termenului ca cheie de array este enervant (nu poți obține primul element ușor folosind cheia 0) și inutil (deja stochezi obiectul $cat
și poți obține ID-ul folosind proprietatea term_id
.

Dacă ca și mine încerci să aplici această funcție la un sub-nivel de categorii, va trebui să transmiți ID-ul nivelului la care te afli pentru ca aceasta să funcționeze. Dar funcționează foarte bine, mulțumesc @popsi.

Folosește wp_list_categories
cu argumentul 'taxonomy' => 'taxonomy'
, această funcție este concepută pentru a crea liste ierarhice de categorii, dar poate fi folosită și cu o taxonomie personalizată.
Exemplu din Codex:
Afișarea termenilor dintr-o taxonomie personalizată
Dacă lista afișată pare plată, este posibil să ai nevoie de puțin CSS pentru a adăuga padding la liste, astfel încât să poți vedea structura lor ierarhică.

Nu cunosc nicio funcție care să facă exact ceea ce doriți, dar puteți construi ceva de genul acesta:
<ul>
<?php $hiterms = get_terms("my_tax", array("orderby" => "slug", "parent" => 0)); ?>
<?php foreach($hiterms as $key => $hiterm) : ?>
<li>
<?php echo $hiterm->name; ?>
<?php $loterms = get_terms("my_tax", array("orderby" => "slug", "parent" => $hiterm->term_id)); ?>
<?php if($loterms) : ?>
<ul>
<?php foreach($loterms as $key => $loterm) : ?>
<li><?php echo $loterm->name; ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
Nu am testat acest cod, dar puteți înțelege ideea. Codul de mai sus va afișa doar două niveluri ierarhice.
EDIT: A, da, puteți folosi wp_list_categories() pentru a obține ceea ce doriți.

De fapt, acest lucru este destul de util, deoarece am nevoie să am link-uri personalizate (cu un parametru GET) pe link-urile termenilor, ceea ce nu pare posibil cu metoda wp_list_categories()
.

Da, această metodă îți va oferi mai mult control asupra rezultatului. Dar ai putea face o operațiune de căutare și înlocuire pe rezultatul funcției wp_list_categories()
pentru a adăuga parametrii tăi GET. Sau chiar mai bine, poți crea un filtru pentru funcție pentru a adăuga elementele dorite. Nu mă întreba cum se face asta, pentru că încă nu am reușit să înțeleg cum :(

Aș sugera să folosești un walker de categorii personalizat cu wp_list_categories
dacă dorești un control mai mare asupra rezultatului, asta va face codul tău mult mai reutilizabil.

Următorul cod va genera un meniu derulant cu termeni, dar poate genera și orice alt element/structură prin editarea variabilei $outputTemplate și a liniilor str_replace:
function get_terms_hierarchical($terms, $output = '', $parent_id = 0, $level = 0) {
//Șablonul de ieșire
$outputTemplate = '<option value="%ID%">%PADDING%%NAME%</option>';
foreach ($terms as $term) {
if ($parent_id == $term->parent) {
//Înlocuirea variabilelor din șablon
$itemOutput = str_replace('%ID%', $term->term_id, $outputTemplate);
$itemOutput = str_replace('%PADDING%', str_pad('', $level*12, ' '), $itemOutput);
$itemOutput = str_replace('%NAME%', $term->name, $itemOutput);
$output .= $itemOutput;
$output = get_terms_hierarchical($terms, $output, $term->term_id, $level + 1);
}
}
return $output;
}
$terms = get_terms('taxonomy', array('hide_empty' => false));
$output = get_terms_hierarchical($terms);
echo '<select>' . $output . '</select>';

Am folosit codul lui @popsi care funcționa foarte bine și l-am făcut mai eficient și mai ușor de citit:
/**
* Sortează recursiv un array de termeni de taxonomie ierarhic. Categoriile copil vor fi
* plasate sub un membru 'children' al termenului lor părinte.
* @param Array $cats obiecte de termeni de taxonomie de sortat
* @param integer $parentId ID-ul părintelui curent în care să fie plasați
*/
function sort_terms_hierarchicaly(Array $cats, $parentId = 0)
{
$into = [];
foreach ($cats as $i => $cat) {
if ($cat->parent == $parentId) {
$cat->children = sort_terms_hierarchicaly($cats, $cat->term_id);
$into[$cat->term_id] = $cat;
}
}
return $into;
}
Utilizare:
$sorted_terms = sort_terms_hierarchicaly($terms);

Când căutam același lucru dar pentru a obține termenii unui post, am compilat în final această soluție care funcționează pentru mine.
Ce face funcția:
• Obține toți termenii unei taxonomii pentru un post specific.
• Pentru o taxonomie ierarhică cu două niveluri (ex: nivel1:'țară' și nivel2:'orașe'), creează un h4 cu nivel1 urmat de o listă neordonată (ul) cu nivel2, și asta pentru toate elementele de nivel1.
• Dacă taxonomia nu este ierarhică, va crea doar o listă neordonată cu toate elementele.
Iată codul (l-am scris pentru mine, așa că am încercat să fie cât mai generic posibil, dar...):
function finishingLister($heTerm){
$myterm = $heTerm;
$terms = get_the_terms($post->ID,$myterm);
if($terms){
$count = count($terms);
echo '<h3>'.$myterm;
echo ((($count>1)&&(!endswith($myterm, 's')))?'s':"").'</h3>';
echo '<div class="'.$myterm.'Wrapper">';
foreach ($terms as $term) {
if (0 == $term->parent) $parentsItems[] = $term;
if ($term->parent) $childItems[] = $term;
};
if(is_taxonomy_hierarchical( $heTerm )){
foreach ($parentsItems as $parentsItem){
echo '<h4>'.$parentsItem->name.'</h4>';
echo '<ul>';
foreach($childItems as $childItem){
if ($childItem->parent == $parentsItem->term_id){
echo '<li>'.$childItem->name.'</li>';
};
};
echo '</ul>';
};
}else{
echo '<ul>';
foreach($parentsItems as $parentsItem){
echo '<li>'.$parentsItem->name.'</li>';
};
echo '</ul>';
};
echo '</div>';
};
};
În final, apelezi funcția astfel (evident, înlocuiește my_taxonomy cu taxa ta): finishingLister('my_taxonomy');
Nu pretind că este perfect, dar cum am spus, funcționează pentru mine.

Am avut această problemă și niciunul dintre răspunsurile de aici nu a funcționat pentru mine, dintr-un motiv sau altul.
Iată versiunea mea actualizată și funcțională.
function locationSelector( $fieldName ) {
$args = array('hide_empty' => false, 'hierarchical' => true, 'parent' => 0);
$terms = get_terms("locations", $args);
$html = '';
$html .= '<select name="' . $fieldName . '"' . 'class="chosen-select ' . $fieldName . '"' . '>';
foreach ( $terms as $term ) {
$html .= '<option value="' . $term->term_id . '">' . $term->name . '</option>';
$args = array(
'hide_empty' => false,
'hierarchical' => true,
'parent' => $term->term_id
);
$childterms = get_terms("locations", $args);
foreach ( $childterms as $childterm ) {
$html .= '<option value="' . $childterm->term_id . '">' . $term->name . ' > ' . $childterm->name . '</option>';
$args = array('hide_empty' => false, 'hierarchical' => true, 'parent' => $childterm->term_id);
$granchildterms = get_terms("locations", $args);
foreach ( $granchildterms as $granchild ) {
$html .= '<option value="' . $granchild->term_id . '">' . $term->name . ' > ' . $childterm->name . ' > ' . $granchild->name . '</option>';
}
}
}
$html .= "</select>";
return $html;
}
Și utilizarea:
$selector = locationSelector('locationSelectClass');
echo $selector;

Această soluție este mai puțin eficientă decât codul lui @popsi, deoarece face o nouă interogare pentru fiecare termen, dar este și mai ușor de utilizat într-un șablon. Dacă site-ul tău utilizează caching, poate, ca și mine, să nu te deranjeze ușoara suprasolicitare a bazei de date.
Nu este nevoie să pregătești un array care va fi umplut recursiv cu termeni. Pur și simplu apelezi funcția în același mod în care ai apela get_terms() (forma nedeprecieată cu doar un array ca argument). Returnează un array de obiecte WP_Term
cu o proprietate suplimentară numită children
.
function get_terms_tree( Array $args ) {
$new_args = $args;
$new_args['parent'] = $new_args['parent'] ?? 0;
$new_args['fields'] = 'all';
// Termenii pentru acest nivel
$terms = get_terms( $new_args );
// Copiii fiecărui termen de pe acest nivel
foreach( $terms as &$this_term ) {
$new_args['parent'] = $this_term->term_id;
$this_term->children = get_terms_tree( $new_args );
}
return $terms;
}
Utilizarea este simplă:
$terms = get_terms_tree([ 'taxonomy' => 'my-tax' ]);

Un ultim lovitură de grație pentru acest cal (similar cu răspunsurile de mai sus) cu un pic mai multă rafinare... permițându-vă să transformați lista într-o listă ierarhic-plată cu un parametru depth
.
În plus, această funcție utilizează clone
pentru a evita modificarea array-ului original.
/**
* Sortează lista de obiecte termeni ierarhic sau ierarhic-plat.
*/
function sort_terms_hierarchically( $terms, $parent_id = 0, $flat = FALSE, $depth = 0 ) {
$data = [];
foreach ( $terms as $term ) {
if ( $parent_id != $term->parent ) continue;
$data_term = clone $term;
$data_term->depth = $depth;
$children = sort_terms_hierarchically( $terms, $term->term_id, $flat, $depth + 1 );
if ( ! $flat ) $data_term->children = $children;
$data[] = $data_term;
if ( $flat ) foreach( $children as $child ) $data[] = $child;
}
return $data;
}
function sort_terms_hierarchically_flat( $terms, $parent_id = 0 ) {
return sort_terms_hierarchically( $terms, $parent_id, TRUE );
}
Utilizarea este următoarea:
$terms = get_terms( [
'taxonomy' => 'regions',
'hide_empty' => FALSE,
] );
$terms_sorted = sort_terms_hierarchically( $terms );
$terms_sorted_flat = sort_terms_hierarchically_flat( $terms );

Asigurați-vă că hierarchical=true
este transmis în apelul dvs. get_terms()
.
Rețineți că hierarchical=true
este valoarea implicită, așa că, de fapt, trebuie doar să vă asigurați că nu a fost suprascrisă pentru a fi false
.

Comentezi la un răspuns lăsat acum aproape doi ani? Serios? De fapt, este un răspuns propus, chiar dacă este formulat ca o întrebare. Să-l editez să fie o afirmație, în loc de o întrebare?

Aici am o listă derulantă cu patru niveluri cu primul element ascuns
<select name="lokalizacja" id="ucz">
<option value="">Toate locațiile</option>
<?php
// Obține termenul exclus după slug
$excluded_term = get_term_by('slug', 'podroze', 'my_travels_places');
$args = array(
'orderby' => 'slug', // Ordonează după slug
'hierarchical' => 'true', // Afișare ierarhică
'exclude' => $excluded_term->term_id, // Exclude termenul specificat
'hide_empty' => '0', // Arată și termenii fără elemente
'parent' => $excluded_term->term_id, // Filtrează după părinte
);
$hiterms = get_terms("my_travels_places", $args);
foreach ($hiterms AS $hiterm) :
echo "<option value='".$hiterm->slug."'".($_POST['my_travels_places'] == $hiterm->slug ? ' selected="selected"' : '').">".$hiterm->name."</option>\n";
// Obține termenii de nivel 2
$loterms = get_terms("my_travels_places", array("orderby" => "slug", "parent" => $hiterm->term_id,'hide_empty' => '0',));
if($loterms) :
foreach($loterms as $key => $loterm) :
echo "<option value='".$loterm->slug."'".($_POST['my_travels_places'] == $loterm->slug ? ' selected="selected"' : '')."> - ".$loterm->name."</option>\n";
// Obține termenii de nivel 3
$lo2terms = get_terms("my_travels_places", array("orderby" => "slug", "parent" => $loterm->term_id,'hide_empty' => '0',));
if($lo2terms) :
foreach($lo2terms as $key => $lo2term) :
echo "<option value='".$lo2term->slug."'".($_POST['my_travels_places'] == $lo2term->slug ? ' selected="selected"' : '')."> - ".$lo2term->name."</option>\n";
endforeach;
endif;
endforeach;
endif;
endforeach;
?>
</select>
<label>Selectează tipul locației</label>
<select name="rodzaj_miejsca" id="woj">
<option value="">Toate tipurile</option>
<?php
// Obține toți termenii ordonați după nume
$theterms = get_terms('my_travels_places_type', 'orderby=name');
foreach ($theterms AS $term) :
echo "<option value='".$term->slug."'".($_POST['my_travels_places_type'] == $term->slug ? ' selected="selected"' : '').">".$term->name."</option>\n";
endforeach;
?>
</select>

Cred că logica este că este o problemă înrudită. Am găsit acest post încercând să înțeleg cum să obțin o listă de verificare ierarhică în stil de categorii și sunt tentat să adaug un răspuns aici acum că am reușit să înțeleg. Nu o voi face totuși pentru că, după cum subliniezi tu, nu răspunde la întrebarea inițială.
