Taxonomía personalizada, get_the_terms, listado en orden de padre > hijo

25 dic 2011, 22:21:36
Vistas: 25.1K
Votos: 10

Tengo una taxonomía jerárquica personalizada que puedo mostrar 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
        )

)

Esta taxonomía siempre tendrá la siguiente forma: Orden (padre) > Familia (hijo del Orden) > Sub-familia (hijo de la Familia)

¿Hay alguna manera rápida y fácil de mostrar estas taxonomías en el orden correcto, para que pueda imprimir la siguiente línea? Orden: <orden>, Familia: <familia>, Sub-familia: <sub-familia>

Gracias de antemano

1
Comentarios

Quien me haya votado negativo, ¿podría explicar por qué?

turbonerd turbonerd
26 dic 2011 15:09:39
Todas las respuestas a la pregunta 7
5

Probablemente hay algunas formas mejores de hacer esto, pero siempre puedes usar tres simples bucles foreach.

Escribí una función de ejemplo que hace bien el trabajo y debería servirte como un buen punto de partida:

function print_taxonomic_ranks( $terms = '' ){

    // verificar entrada
    if ( empty( $terms ) || is_wp_error( $terms ) || ! is_array( $terms ) )
        return;

    // establecer variables de id en 0 para fácil verificación
    $order_id = $family_id = $subfamily_id = 0;

    // obtener orden
    foreach ( $terms as $term ) {
        if ( $order_id || $term->parent )
            continue;
        $order_id  = $term->term_id;
        $order     = $term->name;
    }

    // obtener familia
    foreach ( $terms as $term ) { 
        if ( $family_id || $order_id != $term->parent )
            continue;
        $family_id = $term->term_id;
        $family    = $term->name;
    }

    // obtener subfamilia
    foreach ( $terms as $term ) { 
        if ( $subfamily_id || $family_id != $term->parent ) 
            continue;
        $subfamily_id = $term->term_id;
        $subfamily    = $term->name;
    }

    // salida
    echo "Orden: $order, Familia: $family, Subfamilia: $subfamily";

}

Colócala en tu archivo functions.php y úsala en tus plantillas así:

print_taxonomy_ranks( get_the_terms( $post->ID, 'taxonomic_rank' ) );

NOTA: Recorrer el mismo array tres veces puede sonar un poco tonto, pero por otro lado es una solución rápida y fácil que es simple de leer, extender y mantener.

27 dic 2011 16:19:01
Comentarios

No podría estar más en deuda contigo. Es una respuesta perfecta, ¡y ojalá pudiera darte más reputación!! Si alguien pasa por esta publicación, por favor +1 para que Maugly pueda ser recompensado tanto como me gustaría :)

turbonerd turbonerd
27 dic 2011 16:23:02

No hay problema. Me alegra haber podido ayudar :) También he actualizado el código en mi respuesta y agregué una verificación simple de entrada...

Michal Mau Michal Mau
27 dic 2011 17:10:41

Maugly, gracias por tu respuesta, es casi exactamente lo que necesitaba. ¿Tienes alguna idea sobre cómo usarlo con los enlaces de archivo todavía adjuntos a los términos? Gracias de nuevo.

User User
24 ene 2012 05:55:07

@Adam Echa un vistazo a get_term_link()

Michal Mau Michal Mau
24 ene 2012 12:36:31

¿Qué pasa si la publicación tiene dos categorías principales? Estoy intentando solucionarlo, pero me está tomando demasiado tiempo resolverlo todavía.

Lucas Bustamante Lucas Bustamante
2 jun 2015 08:20:46
1

Tema un poco antiguo pero creo que sigue siendo relevante porque sigue siendo un verdadero dolor de cabeza.

Estoy usando esta función recursiva que toma dos arrays como referencias. Creará un array con la estructura: [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 );

// Registrará los arrays anidados de objetos de términos.
error_log( print_r( $sorted_terms, true ) );

Esta es la única solución que he encontrado que mantiene los objetos de términos y funciona con cualquier nivel de anidamiento.

20 sept 2016 15:25:54
Comentarios

Excelente respuesta, ¡gracias! Sigue siendo útil en 2022. Hay un pequeño error de sintaxis con un desajuste de $this-> al llamar a la función, pero la lógica es perfecta

Ian Ian
7 abr 2022 17:18:01
4

Aunque el enfoque de Maugly parece un poco más legible, ejecutar un bucle 3 veces sobre el array no me parece correcto. Así que aquí hay otro enfoque que podría ser menos legible para algunos, pero funciona sin ejecutar el bucle 3 veces.

function print_taxonomy_ranks( $terms ) {
    // si los términos no son un array o está vacío, no continuar
    if ( ! is_array( $terms ) || empty( $terms ) ) {
        return false;
    }

    foreach ( $terms as $term ) {
        // si el término tiene un padre, establecer el término hijo como atributo en el término padre
        if ( $term->parent != 0 )  {
            $terms[$term->parent]->child = $term;   
        } else {
            // registrar el término padre
            $parent = $term;
        }
    }

    echo "Orden: $parent->name, Familia: {$parent->child->name}, Sub-Familia: {$parent->child->child->name}";
}
28 dic 2011 10:58:36
Comentarios

¡Buen trabajo! Sabía que era posible, simplemente no podía entenderlo en ese momento :) ¡Me gusta tu solución!

Michal Mau Michal Mau
24 ene 2012 14:34:23

Advertencia de voto negativo: Creando objeto por defecto desde un valor vacío

Brad Dalton Brad Dalton
25 sept 2020 13:32:36

@Dev ¿podrías explicar más? ¿De qué objeto estás hablando? Aunque es una respuesta de hace 9 años, me encantaría saber a qué objeto te refieres

Hameedullah Khan Hameedullah Khan
27 sept 2020 07:14:32

Ese es el mensaje de error que obtengo al probar. No especifica qué objeto.

Brad Dalton Brad Dalton
27 sept 2020 07:35:10
2

Tuve una situación donde una publicación podía tener múltiples grupos de categorías, y múltiples hijos dentro de categorías padre, por lo que quería que mi jerarquía reflejara eso. También solo quería unas pocas líneas de código:

$terms = get_the_terms($id, 'department_categories');
foreach($terms as $key => $term){
    if($term->parent != 0){
        $terms[$term->parent]->children[] = $term;
        unset($terms[$key]);
    }
}

Básicamente, después de encontrar el padre de una categoría, lo mueve al objeto padre como un hijo, y luego lo elimina de su posición original en el array. He probado esto usando múltiples hermanos, hijos y diferentes niveles de categorías.

¡Espero que alguien más encuentre esto útil en caso de que solo estén buscando orientación lógica en lugar de un "plugin"!

8 jul 2013 17:57:46
Comentarios

¿Puedes explicar cómo usar eso?

Lucas Bustamante Lucas Bustamante
2 jun 2015 08:22:01

@LucasB Intentaré recordar :P Tenemos un post, y sabemos que su $post->ID es $id y "department_categories" sería la taxonomía.

Simplemente obtiene todos los términos asociados al post y reestructura el array de resultados para que tenga más sentido jerárquico. Una vez reestructurado, puedes mostrarlo como prefieras.

Kaitlyn McDonald Kaitlyn McDonald
2 jun 2015 16:22:36
1

Inspirado por la respuesta de Michal Mau y la respuesta de Manny Fleurmond, aquí está mi solución: Tengo el mismo problema que @Lucas Bustamante: ¿Qué pasa si la publicación tiene dos categorías principales?

Mi solución fue crear otro array de objetos, comparando el term_id y verificando la clave parent

function print_taxonomic_ranks( $terms ){

    if ( ! is_array( $terms ) || empty( $terms ) ) {
        return false;
    }

    $parent_terms = array();

    // obtener solo padres
    foreach ( $terms as $term ) {
        if ($term->parent === 0) {
            $term->child = Array();
            $parent_terms[] = $term;
        }
    }

    // comparar y anidar
    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;
                }
            }
        }
    }

    // mostrar resultados
    foreach ( $parent_terms as $term ) {

        // término padre
        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>';
    }
}

y se verá algo así:

Obtener términos en orden de padres

¡Saludos!

7 abr 2020 01:36:45
Comentarios

La etiqueta <span> no estaba cerrada al final del código echo '.<span>' => echo '.</span>'

Mahdi Afzal Mahdi Afzal
3 feb 2021 15:13:14
0

Gracias Maugly,

Aquí está mi versión modificada de tu código que incluye los enlaces permanentes de términos por si alguien los necesita

function print_show_location( $terms = '' ){

// verificar entrada
if ( empty( $terms ) || is_wp_error( $terms ) || ! is_array( $terms ) )
    return;

// establecer variables de id a 0 para fácil verificación
$country_id = $state_id = $city_id = 0;

// obtener país
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>';
}

// obtener estado/provincia
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>';
}

// obtener ciudad
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>';
}

// salida
echo "$city, $state - $country";

}
25 ene 2012 03:10:23
0

Hay un par de funciones recursivas que uso dependiendo de mis necesidades. Ambas requieren la lista de términos, un ID de padre inicial y un array inicial (generalmente vacío) donde colocar la lista resultante.

PRIMERA VERSIÓN

$terms = [una lista de objetos de términos, usa get_terms o lo que sea]
$ordered_terms = array(); // aquí encontrarás tus términos ordenados, desde la raíz hasta el hijo final

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);
}

SEGUNDA VERSIÓN

$term_ids = [debería ser una lista de IDs en la forma child_id => parent_id]

// una forma rápida de obtener ese tipo de lista es usar WP_Term_query, ejemplo:
//$terms_query = new WP_Term_Query(array(
//  'taxonomy' => 'product_categories'
//  ,'object_ids' => $post->ID
//  ,'hide_empty' => false
//  ,'fields' => 'id=>parent'
//));

$ordered_terms = array(); // aquí encontrarás tus términos ordenados, desde la raíz hasta el hijo final

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);
}

TERCERA VERSIÓN

Esta no es mía, la encontré en algún lugar, probablemente aquí o en stackoverflow. Esta es un poco diferente en la salida, ya que generará una lista de términos en cascada, con solo una raíz, que tiene una propiedad children, que contendrá el siguiente término con su propiedad children y así sucesivamente...

// $ordered_terms se supone que es un array vacío como arriba

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);
  }
}
18 nov 2019 13:47:32