Obtener get_terms por tipo de contenido personalizado

4 jul 2012, 19:29:23
Vistas: 33.6K
Votos: 24

Tengo dos tipos de contenido personalizados 'country' y 'city' y una taxonomía compartida 'flag'.

Si uso:

<?php $flags = get_terms('flag', 'orderby=name&hide_empty=0');

Obtengo una lista de todos los términos en la taxonomía, pero quiero limitar la lista al tipo de contenido 'country'.

¿Cómo puedo hacerlo?


Usando la nueva solución

<?php 
$flags = wpse57444_get_terms('flags',array('parent' => 0,'hide_empty' => 1,'post_types' =>array('country')));
foreach ($flags as $flag) {
    $childTerms = wpse57444_get_terms('flags',array('parent' => $flag->term_id,'hide_empty' => 1,'post_types' =>array('country')));
    foreach ($childTerms as $childTerm) {
        echo $childTerm->name.'<br />';
    }
}
?>

No puedo mostrar $childTerm->name. ¿Por qué?

1
Comentarios

¿Podrías ser un poco más claro?

TheDeadMedic TheDeadMedic
4 jul 2012 19:42:36
Todas las respuestas a la pregunta 5
10
20

Me temo que esto no es posible de forma nativa (¿aún?). Ver este ticket: http://core.trac.wordpress.org/ticket/18106

De manera similar, en la página de administración de taxonomías, el conteo de publicaciones refleja todos los tipos de publicaciones. (Estoy bastante seguro de que hay un ticket para eso también) http://core.trac.wordpress.org/ticket/14084

Ver también, esta publicación relacionada.


Nueva solución

Habiendo escrito la solución de abajo, he descubierto una forma mucho mejor (al menos en el sentido de que puedes hacer más) es usar los filtros proporcionados en la llamada get_terms(). Puedes crear una función envolvente que use get_terms y (condicionalmente) agregue un filtro para manipular la consulta SQL (para restringir por tipo de publicación).

La función toma los mismos argumentos que get_terms($taxonomies, $args). $args toma el argumento adicional de post_types que acepta un array|string de tipos de publicaciones.

Pero no puedo garantizar que todo funcione 'como se espera' (estoy pensando en el relleno del conteo). Parece funcionar usando solo los argumentos predeterminados de $args para get_terms.

function wpse57444_get_terms( $taxonomies, $args=array() ){
    // Analizar $args en caso de que sea una cadena de consulta.
    $args = wp_parse_args($args);

    if( !empty($args['post_types']) ){
        $args['post_types'] = (array) $args['post_types'];
        add_filter( 'terms_clauses','wpse_filter_terms_by_cpt',10,3);

        function wpse_filter_terms_by_cpt( $pieces, $tax, $args){
            global $wpdb;

            // No usar conteo de base de datos
            $pieces['fields'] .=", COUNT(*) " ;

            // Unir tablas adicionales para restringir por tipo de publicación.
            $pieces['join'] .=" INNER JOIN $wpdb->term_relationships AS r ON r.term_taxonomy_id = tt.term_taxonomy_id 
                                INNER JOIN $wpdb->posts AS p ON p.ID = r.object_id ";

            // Restringir por tipo de publicación y agrupar por term_id para el conteo.
            $post_types_str = implode(',',$args['post_types']);
            $pieces['where'].= $wpdb->prepare(" AND p.post_type IN(%s) GROUP BY t.term_id", $post_types_str);

            remove_filter( current_filter(), __FUNCTION__ );
            return $pieces;
        }
    } // endif post_types establecido

    return get_terms($taxonomies, $args);           
}

Uso

$args =array(
    'hide_empty' => 0,
    'post_types' =>array('country','city'),
);

$terms = wpse57444_get_terms('flag',$args);

Solución original

Inspirado en el ticket anterior, (probado y funciona para mí)

function wpse57444_filter_terms_by_cpt($taxonomy, $post_types=array() ){
    global $wpdb;

    $post_types=(array) $post_types;
    $key = 'wpse_terms'.md5($taxonomy.serialize($post_types));
    $results = wp_cache_get($key);

    if ( false === $results ) {
       $where =" WHERE 1=1";
       if( !empty($post_types) ){
            $post_types_str = implode(',',$post_types);
            $where.= $wpdb->prepare(" AND p.post_type IN(%s)", $post_types_str);
       }

       $where .= $wpdb->prepare(" AND tt.taxonomy = %s",$taxonomy);

       $query = "
          SELECT t.*, COUNT(*) 
          FROM $wpdb->terms AS t 
          INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id 
          INNER JOIN $wpdb->term_relationships AS r ON r.term_taxonomy_id = tt.term_taxonomy_id 
          INNER JOIN $wpdb->posts AS p ON p.ID = r.object_id 
          $where
          GROUP BY t.term_id";

       $results = $wpdb->get_results( $query );
       wp_cache_set( $key, $results );
    }        

    return $results;
}

Uso

 $terms = wpse57444_filter_terms_by_cpt('flag',array('country','city'));

o

 $terms = wpse57444_filter_terms_by_cpt('flag','country');
4 jul 2012 20:00:56
Comentarios

Funciona, pero ¿qué puedo hacer con mis $args? Me refiero a... parent=0&orderby=name&hide_empty=0

user1443216 user1443216
4 jul 2012 22:11:24

no - esto debe ser un array: $args = array('parent'=>0,'orderby'=>'name','hide_empty'=>0);. Voy a editar esto para permitir cadenas de consulta...

Stephen Harris Stephen Harris
4 jul 2012 22:44:51

¿Dónde puedo poner mis $args en este ejemplo: $terms = wpse57444_filter_terms_by_cpt('flag',array('country','city'));?

user1443216 user1443216
4 jul 2012 22:59:45

No puedes en ese, solo en la nueva solución:wpse57444_get_terms()

Stephen Harris Stephen Harris
4 jul 2012 23:21:09

@user1443216 $args es el segundo argumento. Allí solo pones wpse57444_get_terms( 'flag', array( 'country', 'city' ) );

kaiser kaiser
4 jul 2012 23:21:12

@StephenHarris Incluso el núcleo intenta eliminar todo lo que parezca una cadena de consulta. Solo añade peso innecesario. Ya eliminé este tipo de funcionalidad de todo mi código después de una conversación con uno de los desarrolladores principales.

kaiser kaiser
4 jul 2012 23:22:25

Si uso esto: $flags = wpse57444_get_terms('flags',array('parent' => 0,'hide_empty' => 1,'post_types' =>array('country'))); foreach ($flags as $flag) { $childTerms = wpse57444_get_terms('flags',array('parent' => $flag->term_id,'hide_empty' => 1,'post_types' =>array('country'))); foreach ($childTerms as $childTerm) { echo $childTerm->name.'<br />'; } } no puedo hacer echo de $childTerm->name

user1443216 user1443216
7 jul 2012 17:03:36

para obtener un conteo correcto tuve que actualizar esta línea a: $pieces['fields'] .= ", COUNT(*) as count ";

locomo locomo
9 mar 2018 23:34:12

¿Todavía no hay una mejor manera casi en 2019? Eso es triste :)

trainoasis trainoasis
14 dic 2018 00:20:26

La solución anterior funciona muy bien con 1 tipo de publicación. Con muchos tipos de publicaciones tenemos un problema con prepare. Parece que no sanitiza el $post_types_str que generará IN( 'whitepaper, blog' ) en lugar de IN( 'whitepaper', 'blog' ). Por favor revisa este artículo para una solución alternativa enlace

Redox Redox
4 nov 2019 10:51:21
Mostrar los 5 comentarios restantes
1

Dos tipos de entradas personalizadas 'country' y 'city' y una taxonomía compartida 'flag'. Quieres limitar la lista al tipo de entrada 'country'.

Aquí hay una solución más simple:

$posts_in_post_type = get_posts( array(
    'fields' => 'ids',
    'post_type' => 'country',
    'posts_per_page' => -1,
) );
$terms = wp_get_object_terms( $posts_in_post_type, 'flag', array( 'ids' ) ); ?>
7 nov 2017 14:44:29
Comentarios

Esta parece ser la forma recomendada según este ticket de trac: https://core.trac.wordpress.org/ticket/18106#comment:7. Ten en cuenta que esto puede ser muy lento, por lo que probablemente necesitarás implementar algún tipo de caché si tu sitio tiene muchas publicaciones.

Jules Jules
11 nov 2020 16:25:33
1

La respuesta de @stephen-harris anterior solo funcionó parcialmente para mí. Si intentaba usarla dos veces en la página, no funcionaba. Además, la idea de enterrar consultas mysql de esa manera me preocupa: creo que es mejor práctica usar métodos principales para lograr una solución, para evitar conflictos con futuras actualizaciones de WP. Aquí está mi solución, basada en el comentario #7 en el ticket Trac que él referenció

function get_terms_by_custom_post_type( $post_type, $taxonomy ){
  $args = array( 'post_type' => $post_type);
  $loop = new WP_Query( $args );
  $postids = array();
  // construir un array de IDs de posts
  while ( $loop->have_posts() ) : $loop->the_post();
    array_push($postids, get_the_ID());
  endwhile;
  // obtener valores de taxonomía basados en el array de IDs
  $regions = wp_get_object_terms( $postids,  $taxonomy );
  return $regions;
}

Uso:

$terms = get_terms_by_custom_post_type('country','flag');

Esto funciona solo para un tipo de post y una taxonomía, porque eso es lo que necesitaba, pero no sería muy difícil modificar esto para aceptar múltiples valores.

Hubo alguna mención en ese hilo de Trac de que esto podría no escalar bien, pero estoy trabajando en una escala bastante pequeña y no he tenido problemas con la velocidad.

7 mar 2017 18:52:27
Comentarios

esta solución me parece más "nativa" - de cualquier forma ->

deberías llamar a "wp_reset_postdata()" justo después del "endwhile" del bucle: https://wordpress.stackexchange.com/questions/144343/wp-reset-postdata-or-wp-reset-query-after-a-custom-loop

Thomas Fellinger Thomas Fellinger
22 nov 2017 12:04:47
0

[editar] Este es un comentario sobre la excelente respuesta de Stephen Harris.

No devuelve ningún término si se usa con múltiples tipos de publicación como $flags = wpse57444_get_terms('flags', array('post_types' => array('country','city')));. Esto ocurre porque $wpdb->prepare sanitiza la cadena $post_types_str a p.post_type IN('country,city') cuando debería ser p.post_type IN('country','city'). Consulte este ticket: 11102. Utilice la solución de este tema para solucionarlo: https://stackoverflow.com/a/10634225

13 feb 2013 16:42:10
0

También intenté usar la respuesta de @Stephen Harris, pero la consulta que necesitaba era bastante difícil de escribir como una sola consulta y utilizando los filtros.

Además, también necesitaba usar esa función múltiples veces en la misma página y resolví el problema declarando la función wpse_filter_terms_by_cpt fuera de la función wrapper.

De cualquier manera, en mi opinión la respuesta de @Mark Pruce encaja mejor, por las mismas razones que él mencionó, aunque requiere que hagas una consulta adicional (y el bucle relacionado) para preparar los argumentos para la función wp_get_object_terms.

3 ago 2017 12:31:57