Tassonomia personalizzata, get_the_terms, elenco in ordine genitore > figlio
Ho una tassonomia gerarchica personalizzata che posso visualizzare usando print_r(get_the_terms( $post->ID, 'taxonomic_rank' ));
:
Array
(
[46] => stdClass Object
(
[term_id] => 46
[name] => Aplocheilidae
[slug] => aplocheilidae
[term_group] => 0
[term_taxonomy_id] => 53
[taxonomy] => taxonomic_ranks
[description] =>
[parent] => 39
[count] => 1
[object_id] => 443
)
[47] => stdClass Object
(
[term_id] => 47
[name] => Aplocheilus
[slug] => aplocheilus
[term_group] => 0
[term_taxonomy_id] => 54
[taxonomy] => taxonomic_ranks
[description] =>
[parent] => 46
[count] => 1
[object_id] => 443
)
[39] => stdClass Object
(
[term_id] => 39
[name] => Cyprinodontiformes
[slug] => cyprinodontiformes
[term_group] => 0
[term_taxonomy_id] => 52
[taxonomy] => taxonomic_ranks
[description] =>
[parent] => 0
[count] => 1
[object_id] => 443
)
)
Questa tassonomia avrà sempre la seguente forma: Ordine (genitore) > Famiglia (figlio dell'Ordine) > Sotto-famiglia (figlio della Famiglia)
Esiste un modo rapido e semplice per visualizzare queste tassonomie nell'ordine corretto, in modo da poter stampare la seguente riga? Ordine: <ordine>, Famiglia: <famiglia>, Sotto-famiglia: <sotto-famiglia>
Grazie in anticipo

Probabilmente ci sono modi migliori per farlo, ma puoi sempre utilizzare tre semplici cicli foreach
.
Ho scritto una funzione di esempio che svolge bene il lavoro e dovrebbe servirti come un buon punto di partenza:
function print_taxonomic_ranks( $terms = '' ){
// controlla l'input
if ( empty( $terms ) || is_wp_error( $terms ) || ! is_array( $terms ) )
return;
// imposta le variabili ID a 0 per un controllo facile
$order_id = $family_id = $subfamily_id = 0;
// ottiene l'ordine
foreach ( $terms as $term ) {
if ( $order_id || $term->parent )
continue;
$order_id = $term->term_id;
$order = $term->name;
}
// ottiene la famiglia
foreach ( $terms as $term ) {
if ( $family_id || $order_id != $term->parent )
continue;
$family_id = $term->term_id;
$family = $term->name;
}
// ottiene la sottofamiglia
foreach ( $terms as $term ) {
if ( $subfamily_id || $family_id != $term->parent )
continue;
$subfamily_id = $term->term_id;
$subfamily = $term->name;
}
// output
echo "Ordine: $order, Famiglia: $family, Sottofamiglia: $subfamily";
}
Inseriscila nel tuo file functions.php
e usala nei tuoi template in questo modo:
print_taxonomy_ranks( get_the_terms( $post->ID, 'taxonomic_rank' ) );
NOTA: Ciclare lo stesso array tre volte può sembrare un po' stupido, ma d'altra parte è una soluzione rapida e semplice che è facile da leggere, estendere e mantenere.

Non potrei essere più in debito con te. È una risposta perfetta, e vorrei poterti dare più reputazione!! Se qualcuno passa da questo post, per favore +1 così Maugly può essere ricompensato quanto vorrei io :)

Nessun problema. Sono contento di aver potuto aiutare :) Ho anche aggiornato il codice nella mia risposta e aggiunto un semplice controllo dell'input...

Maugly, grazie per la tua risposta, è quasi esattamente quello che mi serviva - hai qualche idea su come usarlo con i link degli archivi ancora collegati ai termini? Grazie ancora

@Adam Dai un'occhiata a get_term_link()

Argomento un po' vecchio ma penso sia ancora rilevante dato che è ancora una gran seccatura.
Sto usando questa funzione ricorsiva che prende due array come riferimenti. Creerà un array con la struttura:
[term_id] => term_object->children->child_terms_array->children->child_terms_array
.
<?php
function sort_terms_hierarchically( array &$terms, array &$into, $parent_id = 0 ) {
foreach ( $terms as $i => $term ) {
if ( $term->parent == $parent_id ) {
$into[$term->term_id] = $term;
unset( $terms[ $i ] );
}
}
foreach ( $into as $top_term ) {
$top_term->children = array();
$this->sort_terms_hierarchically( $terms, $top_term->children, $top_term->term_id );
}
}
$terms = get_the_terms( 'taxslug', $post );
$sorted_terms = array();
sort_terms_hierarchically( $terms, $sorted_terms );
// Registrerà gli array annidati di oggetti term.
error_log( print_r( $sorted_terms, true ) );
Questa è l'unica soluzione che ho trovato che mantiene gli oggetti term e funziona con qualsiasi livello di annidamento.

Anche se l'approccio di Maugly sembra un po' più leggibile, eseguire un ciclo 3 volte sull'array non mi sembra corretto. Ecco quindi un altro approccio che potrebbe essere meno leggibile per alcuni ma funziona senza eseguire il ciclo 3 volte.
function print_taxonomy_ranks( $terms ) {
// se terms non è un array o è vuoto, non procedere
if ( ! is_array( $terms ) || empty( $terms ) ) {
return false;
}
foreach ( $terms as $term ) {
// se il termine ha un genitore, imposta il termine figlio come attributo nel termine genitore
if ( $term->parent != 0 ) {
$terms[$term->parent]->child = $term;
} else {
// registra il termine genitore
$parent = $term;
}
}
echo "Ordine: $parent->name, Famiglia: {$parent->child->name}, Sottofamiglia: {$parent->child->child->name}";
}

Ottimo lavoro! Sapevo che fosse possibile, ma all'epoca non riuscivo a capire come :) Mi piace la tua soluzione!

Avviso di downvote: Creazione di un oggetto predefinito da un valore vuoto

@Dev potresti per favore spiegare meglio? Di quale oggetto stai parlando. Anche se è una risposta di 9 anni fa, mi piacerebbe sapere a quale oggetto ti riferisci

Mi sono trovato in una situazione in cui un articolo poteva essere etichettato con più gruppi di categorie, e più figli all'interno di categorie genitore, quindi volevo che la mia gerarchia riflettesse questo. Volevo anche solo poche righe di codice:
$terms = get_the_terms($id, 'department_categories');
foreach($terms as $key => $term){
if($term->parent != 0){
$terms[$term->parent]->children[] = $term;
unset($terms[$key]);
}
}
In sostanza, dopo aver trovato il genitore di una categoria, lo sposta nell'oggetto genitore come figlio, e poi lo rimuove dalla sua posizione originale nell'array. Ho testato questo codice utilizzando più fratelli, figli e diversi livelli di categorie.
Spero che qualcun altro trovi utile questo codice nel caso stia cercando una guida logica piuttosto che un "plugin"!

@LucasB Proverò a ricordare :P Abbiamo un post, e sappiamo che il suo $post->ID è $id e "department_categories" sarebbe la tassonomia.
Semplicemente ottiene tutti i termini associati al post e ristruttura l'array dei risultati per avere più senso gerarchico. Una volta ristrutturato, sei libero di visualizzarlo come preferisci.

Ispirato dalla risposta di Michal Mau e dalla risposta di Manny Fleurmond, ecco la mia soluzione: Ho lo stesso problema di @Lucas Bustamante: cosa succede se il post ha due categorie genitore?
La mia soluzione è stata creare un altro array di oggetti, confrontando il term_id
e verificando la chiave parent
function print_taxonomic_ranks( $terms ){
if ( ! is_array( $terms ) || empty( $terms ) ) {
return false;
}
$parent_terms = array();
// ottieni solo i genitori
foreach ( $terms as $term ) {
if ($term->parent === 0) {
$term->child = Array();
$parent_terms[] = $term;
}
}
// confronta e annida
foreach ( $terms as $term ) {
if ($term->parent != 0) {
foreach ($parent_terms as $key => $value) {
if ($term->parent === $value->term_id) {
$parent_terms[$key]->child[] = $term;
}
}
}
}
// mostra i risultati
foreach ( $parent_terms as $term ) {
// termine genitore
echo '<span class="d-block">'.$term->name.'';
if ($term->child) {
$i = 1;
foreach ( $term->child as $child ) {
//echo '<span class="text-danger">'.$i.'</span>';
echo ($i <= 1)? ": " : "";
echo '<span class="font-weight-normal">'.$child->name.'</span>';
echo ($i < count($term->child))? ", " : "";
$i++;
}
}
echo '.<span>';
}
}
e apparirà qualcosa come questo:
Saluti!

Grazie Maugly,
Ecco la mia versione modificata del tuo codice che include i permalink dei termini per chi ne avesse bisogno
function print_show_location( $terms = '' ){
// controllo dell'input
if ( empty( $terms ) || is_wp_error( $terms ) || ! is_array( $terms ) )
return;
// imposta le variabili ID a 0 per un controllo più semplice
$country_id = $state_id = $city_id = 0;
// ottieni il paese
foreach ( $terms as $term ) {
if ( $country_id || $term->parent )
continue;
$country_id = $term->term_id;
$country_slug = $term->slug;
$country = '<a href="'.get_term_link($country_slug, 'location').'">'.$term->name.'</a>';
}
// ottieni lo stato
foreach ( $terms as $term ) {
if ( $state_id || $country_id != $term->parent )
continue;
$state_id = $term->term_id;
$state_slug = $term->slug;
$state = '<a href="'.get_term_link($state_slug, 'location').'">'.$term->name.'</a>';
}
// ottieni la città
foreach ( $terms as $term ) {
if ( $city_id || $state_id != $term->parent )
continue;
$city_id = $term->term_id;
$city_slug = $term->slug;
$city = '<a href="'.get_term_link($city_slug, 'location').'">'.$term->name.'</a>';
}
// output
echo "$city, $state - $country";
}

Ci sono un paio di funzioni ricorsive che utilizzo a seconda delle esigenze. Entrambe richiedono la lista dei termini, un ID genitore di partenza e un array iniziale (generalmente vuoto) dove inserire la lista risultante.
PRIMA VERSIONE
$terms = [una lista di oggetti termini, usa get_terms o altro]
$ordered_terms = array(); // qui troverai i termini ordinati, dalla radice al figlio finale
function list_terms_by_parent($parent_id = 0, &$terms, &$ordered_terms){
$root_parent = $parent_id;
foreach($terms as $index => $term){
if($term->parent == (int) $parent_id){
$ordered_terms[$term->term_id] = $term;
$root_parent = $term->term_id;
unset($terms[$index]);
}
}
if(!empty($terms)) list_terms_by_parent($root_parent, $terms, $ordered_terms);
}
SECONDA VERSIONE
$term_ids = [dovrebbe essere una lista di ID nella forma child_id => parent_id]
// un modo rapido per ottenere questo tipo di lista è usare WP_Term_query, esempio:
//$terms_query = new WP_Term_Query(array(
// 'taxonomy' => 'product_categories'
// ,'object_ids' => $post->ID
// ,'hide_empty' => false
// ,'fields' => 'id=>parent'
//));
$ordered_terms = array(); // qui troverai i termini ordinati, dalla radice al figlio finale
function list_term_ids_by_parent($parent_id = 0, &$term_ids, &$ordered_terms){
$child_id = array_search($parent_id, $term_ids);
if($child_id){
$ordered_terms[] = $child_id;
unset($term_ids[$child_id]);
}
if(!empty($term_ids)) order_terms($child_id, $term_ids, $ordered_terms);
}
TERZA VERSIONE
Questa non è mia, l'ho trovata da qualche parte, probabilmente qui o su stackoverflow. Questa è leggermente diversa nell'output, poiché genererà una lista di termini a cascata, con una sola radice, che ha una proprietà children, che conterrà il termine successivo con la sua proprietà children e così via..
// $ordered_terms dovrebbe essere un array vuoto come sopra
function sort_terms_hierarchically(&$terms, &$ordered_terms, $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();
sort_terms_hierarchically($cats, $topCat->children, $topCat->term_id);
}
}
