¿Cómo listar solo los términos hijos de una taxonomía y no sus padres?

24 jul 2011, 15:51:21
Vistas: 94.2K
Votos: 12

Estoy creando un menú de selección de edad en el admin, poblado desde una taxonomía de age. La taxonomía es jerárquica de la siguiente manera:

  • 18-25 (padre, ID 183)
    • 18 (hijo)
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 26-30 (padre, ID 184)
    • 26
    • 27
    • 28
    • 29
    • 30

Me gustaría listar solo los hijos (18, 19 etc) y no los padres (18-25, 26-30) etc. Actualmente estoy usando get_terms con el argumento parent, pero no acepta más de 1 ID padre. Esto es lo que tengo hasta ahora, que muestra los hijos de 18-25.

    $ages = get_terms( 'age', array(
        'hide_empty' => 0,
        'parent' => '183',
    ));

Esto es lo que quiero hacer, pero no está soportado. También lo he intentado con un array pero no funciona.

    $ages = get_terms( 'age', array(
        'hide_empty' => 0,
        'parent' => '183,184',
    ));

Veo que existe una función get_term_children pero tampoco estoy seguro de cómo usarla ya que parece que solo acepta un valor también. Por ejemplo: En este ejemplo construiría una lista desordenada pero podría modificarla para un menú select.

<?php
    // ID del término
    $termID = 183;
    // Nombre de la taxonomía
    $taxonomyName = "age";
    $termchildren = get_term_children( $termID, $taxonomyName );

    echo '<ul>';
    foreach ($termchildren as $child) {
    $term = get_term_by( 'id', $child, $taxonomyName );
    echo '<li><a href="' . get_term_link( $term->name, $taxonomyName ) . '">' . $term->name . '</a></li>';
    }
    echo '</ul>';
?> 
3
Comentarios

¿Llamar a get_terms dos veces y fusionar los dos arrays de resultados?

t31os t31os
24 jul 2011 18:21:36

@Mark Gracias, pensé en hacerlo de esta manera, pero incluso si combino los ID's en un array, no veo cómo funcionaría porque sería lo mismo que listarlos manualmente - 183, 184, lo cual no funciona.

Andrew Andrew
25 jul 2011 02:43:27

Después de leer la respuesta aceptada, ahora me doy cuenta de que tu pregunta no estaba del todo clara, por lo que parece querías todos los términos, excluyendo los de nivel superior.. (lo cual puedes hacer con una sola llamada a get_terms). Tu pregunta sonaba como si quisieras todos los hijos de 2 términos padres particulares..

t31os t31os
26 jul 2011 19:03:00
Todas las respuestas a la pregunta 6
4
19

Esto debería funcionar para ti:

$taxonomyName = "age";
// Esto obtiene solo los términos de nivel superior. Se hace estableciendo parent en 0.
$parent_terms = get_terms(
  $taxonomyName,
  array(
    'parent' => 0,
    'orderby' => 'slug',
    'hide_empty' => false
  )
);

echo '<ul>';
foreach ( $parent_terms as $pterm ) {
  // Obtener los términos hijos
  $terms = get_terms(
    $taxonomyName,
    array(
      'parent' => $pterm->term_id,
      'orderby' => 'slug',
      'hide_empty' => false
    )
  );
  foreach ( $terms as $term ) {
    echo '<li><a href="'. get_term_link($term) . '">'. $term->name .'</a></li>';
  }
}
echo '</ul>';
25 jul 2011 06:46:05
Comentarios

Gracias @Manny, funcionó de maravilla. Tomé lo que proporcionaste y lo integré en el menú desplegable que estaba buscando. Excelente material.

Andrew Andrew
25 jul 2011 07:57:08

No hay problema. Me alegra que te haya funcionado.

Manny Fleurmond Manny Fleurmond
25 jul 2011 08:21:17

Consulta la respuesta de karimhossenbux más abajo para una solución mucho más eficiente.

dotancohen dotancohen
15 mar 2016 18:47:22

$term->name no es válido en get_term_link(), solo acepta el ID del término, slug u objeto

Pieter Goosen Pieter Goosen
15 mar 2016 20:03:04
4
17

También podrías hacer:

$terms = get_terms($taxonomyName);
foreach($terms as $term) {
    if ($term->parent != 0) { // evitar categorías padre
        //tus instrucciones aquí
    }
}

He notado que los elementos padre tienen el campo "parent" igual a 0, y un hijo tiene el ID de su padre en este campo.

15 mar 2016 18:32:03
Comentarios

La respuesta aceptada ejecuta N llamadas a get_terms() y se ejecuta en tiempo polinómico. Esta respuesta ejecuta una única llamada a get_terms() y se ejecuta en tiempo lineal. Esta es una respuesta mucho mejor.

dotancohen dotancohen
15 mar 2016 18:46:41

@dotancohen Puedes hacer esto sin tener que excluir términos de nivel superior en la salida, puedes eliminar los términos de nivel superior con el parámetro wpse_exclude_top añadido a los argumentos de la consulta ;-). Sin embargo, estoy de acuerdo en que esto es más rápido que la respuesta aceptada

Pieter Goosen Pieter Goosen
15 mar 2016 20:05:07

@PieterGoosen: Gracias Pieter. Acabo de buscar en el código fuente de la versión 4.4.2 las cadenas wpse_exclude_top y exclude_top pero no las encontré. Tampoco google sabe sobre eso. ¿Dónde está documentado?

dotancohen dotancohen
16 mar 2016 10:23:06

@dotancohen en mi respuesta ;-)

Pieter Goosen Pieter Goosen
16 mar 2016 10:24:53
1

Podemos excluir los padres de nivel superior filtrándolos utilizando el filtro terms_clauses para alterar la consulta SQL antes de que se ejecute. De esta manera, no necesitamos omitir los padres en el bucle foreach final, ya que no estarán en el array de términos devuelto. Esto nos ahorra trabajo y código innecesarios.

Puedes probar lo siguiente:

add_filter( 'terms_clauses', function (  $pieces, $taxonomies, $args )
{
    // Verifica si nuestro argumento personalizado está establecido y es igual a 1, si no, salimos
    if (    !isset( $args['wpse_exclude_top'] ) 
         || 1 !== $args['wpse_exclude_top']
    )
        return $pieces;

    // Todo verificado, eliminamos los padres
    $pieces['where'] .= ' AND tt.parent > 0';

    return $pieces;
}, 10, 3 );

Para excluir los padres de nivel superior, ahora podemos pasar 'wpse_exclude_top' => 1 con nuestro array de argumentos. El nuevo parámetro wpse_exclude_top es compatible con el filtro anterior.

$terms = get_terms( 'category', ['wpse_exclude_top' => 1] );
if (    $terms
     && !is_wp_error( $terms )
) {
    echo '<ul>';
        foreach ($terms as $term) {
            echo '<li><a href="' . get_term_link( $term ) . '">' . $term->name . '</a></li>';  
        }
    echo '</ul>';
}

Solo una nota: get_term_link() no acepta el nombre del término, solo el slug, ID o el objeto de término completo. Por rendimiento, siempre pasa el objeto de término a get_term_link() si el objeto de término está disponible (como en este caso).

15 mar 2016 20:01:59
Comentarios

Hola, ¿alguna idea de cómo solucionar este problema? stackoverflow.com/q/70327174/9036446

evavienna evavienna
17 dic 2021 07:22:10
3

¿Por qué no puedes simplemente establecer el argumento childless como verdadero?

$ages = get_terms( array(
    'taxonomy' => 'age',
    'childless' => true
    )
);
20 oct 2016 02:20:19
Comentarios

establecer el argumento childless como verdadero significa que solo puedes profundizar 1 nivel, por lo que esto no funciona para taxonomías con 3 o más niveles.

GeckoSEO GeckoSEO
23 abr 2018 18:58:19

¡Esto funcionó perfectamente para mis necesidades! ¡Gracias!

jsmod jsmod
11 oct 2020 19:54:13

También devolverá padres sin hijos en lugar de hijos sin padres.

certainlyakey certainlyakey
2 ene 2021 23:15:05
0
$taxonomia = 'edad';
foreach( get_terms( [ 'taxonomy' => $taxonomia, 'parent' => 0 ] ) as $padre )
{
    foreach( get_terms( [ 'taxonomy' => $taxonomia, 'parent' => $padre->term_id ] ) as $hijo )
    {
        // hacer algo
        print_r( $hijo );
    }
}
16 abr 2024 13:16:09
2
-1

Si necesitas mostrar múltiples términos hijos de un padre, puedes probar esto. Muestra los términos hijos de los IDs mencionados.

$termIds = array(367, 366, 365, 364, 363, 362);
$taxonomyName = "age";

$args = array(
    'orderby'           => 'term_id', 
    'order'             => 'DESC',                      
    'hide_empty'        => false,                       
    'childless'         => false,
); 

$terms = get_terms( $taxonomyName, $args );

if ( ! empty( $terms ) && ! is_wp_error( $terms ) ){
$inc = 1;
foreach ( $terms as $term ) {                           
 if (in_array($term->parent, $termIds)) {
  echo '<option value="'.$term->term_id.'"><font><font>'.$term->name.'</font></font></option>';
    }
  }
}
29 dic 2015 15:16:42
Comentarios

Por favor, explica por qué crees que este código debería funcionar. Además, estoy bastante seguro de que una solución con código fijo no es la mejor manera.

s_ha_dum s_ha_dum
29 dic 2015 16:23:58

La pregunta menciona con ids, por esta razón he respondido pensando en eso.

Jakir Hossain Jakir Hossain
30 dic 2015 17:23:33