Obtenga términos por taxonomía Y post_type

17

Tengo 2 tipos de publicaciones personalizadas 'marcadores' y 'fragmentos' y una etiqueta de taxonomía compartida. Puedo generar una lista de todos los términos en la taxonomía con get_terms (), pero no puedo entender cómo limitar la lista al tipo de publicación. Lo que básicamente estoy buscando es algo como esto:

get_terms(array('taxonomy' => 'tag', 'post_type' => 'snippet'));

¿Hay una manera de lograr esto? ¡Las ideas son muy apreciadas!

Oh, estoy en WP 3.1.1

Gavin Hewitt
fuente

Respuestas:

11

Aquí hay otra forma de hacer algo similar, con una consulta SQL:

static public function get_terms_by_post_type( $taxonomies, $post_types ) {

    global $wpdb;

    $query = $wpdb->prepare(
        "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 p.post_type IN('%s') AND tt.taxonomy IN('%s')
        GROUP BY t.term_id",
        join( "', '", $post_types ),
        join( "', '", $taxonomies )
    );

    $results = $wpdb->get_results( $query );

    return $results;

}
Braydon
fuente
¡Si! Esto hace exactamente lo que quiero.
Gavin Hewitt, el
print_r(get_terms_by_post_type(array('category') , array('event') ));espectáculosWarning: Missing argument 2 for wpdb::prepare()
devo
Podría estar equivocado, pero fuera de mi cabeza, no creo que esas declaraciones de 'unión' funcionen, es decir, solo funcionarían si se pasan matrices de un solo valor. Esto se debe a que la función de preparación escaparía a todas las comillas simples generadas, y consideraría cada 'unión' completa de una cadena.
Codesmith
14

Entonces sucede que necesitaba algo así para un proyecto en el que estoy trabajando. Simplemente escribí una consulta para seleccionar todas las publicaciones de un tipo personalizado, luego verifiqué cuáles son los términos reales de mi taxonomía que están utilizando.

Luego obtuve todos los términos de esa taxonomía usando get_terms()y luego solo usé los que estaban en ambas listas, lo envolví en una función y terminé.

Pero luego necesitaba más que solo los ID: necesitaba los nombres, así que agregué un nuevo argumento llamado $fieldspara poder decirle a la función qué devolver. Luego supuse que get_termsacepta muchos argumentos y mi función se limitó a términos simples que están siendo utilizados por un tipo de publicación, así que agregué una ifdeclaración más y listo:

La función:

/* get terms limited to post type 
 @ $taxonomies - (string|array) (required) The taxonomies to retrieve terms from. 
 @ $args  -  (string|array) all Possible Arguments of get_terms http://codex.wordpress.org/Function_Reference/get_terms
 @ $post_type - (string|array) of post types to limit the terms to
 @ $fields - (string) What to return (default all) accepts ID,name,all,get_terms. 
 if you want to use get_terms arguments then $fields must be set to 'get_terms'
*/
function get_terms_by_post_type($taxonomies,$args,$post_type,$fields = 'all'){
    $args = array(
        'post_type' => (array)$post_type,
        'posts_per_page' => -1
    );
    $the_query = new WP_Query( $args );
    $terms = array();
    while ($the_query->have_posts()){
        $the_query->the_post();
        $curent_terms = wp_get_object_terms( $post->ID, $taxonomy);
        foreach ($curent_terms as $t){
          //avoid duplicates
            if (!in_array($t,$terms)){
                $terms[] = $c;
            }
        }
    }
    wp_reset_query();
    //return array of term objects
    if ($fields == "all")
        return $terms;
    //return array of term ID's
    if ($fields == "ID"){
        foreach ($terms as $t){
            $re[] = $t->term_id;
        }
        return $re;
    }
    //return array of term names
    if ($fields == "name"){
        foreach ($terms as $t){
            $re[] = $t->name;
        }
        return $re;
    }
    // get terms with get_terms arguments
    if ($fields == "get_terms"){
        $terms2 = get_terms( $taxonomies, $args );
        foreach ($terms as $t){
            if (in_array($t,$terms2)){
                $re[] = $t;
            }
        }
        return $re;
    }
}

Uso:

Si solo necesita una lista de ID de términos, entonces:

$terms = get_terms_by_post_type('tag','','snippet','ID');

Si solo necesita una lista de nombres de términos, entonces:

$terms = get_terms_by_post_type('tag','','snippet','name');

Si solo necesita una lista de objetos de términos, entonces:

$terms = get_terms_by_post_type('tag','','snippet');

Y si necesita usar argumentos adicionales de get_terms como: orderby, order, jerárquico ...

$args = array('orderby' => 'count', 'order' => 'DESC',  'hide_empty' => 1);
$terms = get_terms_by_post_type('tag',$args,'snippet','get_terms');

¡Disfrutar!

Actualizar:

Para arreglar el recuento de términos al cambio de tipo de publicación específico:

foreach ($current_terms as $t){
          //avoid duplicates
            if (!in_array($t,$terms)){
                $terms[] = $t;
            }
        }

a:

foreach ($current_terms as $t){
    //avoid duplicates
    if (!in_array($t,$terms)){
        $t->count = 1;
        $terms[] = $t;
    }else{
        $key = array_search($t, $terms);
        $terms[$key]->count = $terms[$key]->count + 1;
    }
}
Bainternet
fuente
¿no sería mejor usarlo en (array) $argslugar de una lista de 4 $ vars? Esto le permitiría no preocuparse por el orden en que arroja los argumentos, por lo que algo así como get_terms_by_post_type( $args = array( 'taxonomies', 'args', 'post_type', 'fields' => 'all') )y luego llamarlos dentro de la función con $args['taxonomies']. Esto lo ayudaría a evitar agregar valores vacíos y tener que recordar el orden de sus argumentos. También sugeriría usar comillas simples en lugar de dobles. Los vi sonar hasta cinco veces más rápido.
kaiser
1
@kaiser: las cadenas entre comillas dobles deben analizarse, donde los valores entre comillas simples siempre se tratan como literales. Cuando usa variables en una cadena tiene sentido y está perfectamente bien usar comillas dobles, pero para valores de cadena no variables, las comillas simples son más ideales (porque no necesitarán analizarse) y un poco más rápido (nosotros ' estamos hablando de milisegundos en la mayoría de los casos).
t31os
@ t31os - Absolutamente correcto. Todavía prefiero 'this is my mood: '.$valuesobre "this is my mood: $value", debido a la legibilidad. Cuando se trata de velocidad: no es un poco, lo medí hasta cinco veces. Y cuando usa comillas dobles en todo su tema en todas partes, se resumirán rápidamente cuando reciba muchas solicitudes. De todos modos, lo dejaste claro.
kaiser
@ t31os De una discusión volví a medir la velocidad de "vs. 'y me equivoqué. La diferencia está muy lejos de todo lo que cualquiera notaría.
kaiser
1
+1 buena función! 2 errores tipográficos: $ taxonomías se usa en la función $ taxonomía y $ términos [] = $ c; tiene que ser $ términos [] = $ t;
Rob Vermeer
8

Escribí una función que le permite pasar post_typela $argsmatriz a la get_terms()función:

HT a @braydon por escribir el SQL.

 /**
 * terms_clauses
 *
 * filter the terms clauses
 *
 * @param $clauses array
 * @param $taxonomy string
 * @param $args array
 * @return array
**/
function terms_clauses($clauses, $taxonomy, $args)
{
    global $wpdb;

    if ($args['post_type'])
    {
        $clauses['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";
        $clauses['where'] .= " AND p.post_type='{$args['post_type']}'"; 
    }
    return $clauses;
}
add_filter('terms_clauses', 'terms_clauses', 10, 3);
jessica
fuente
7

Gran pregunta y respuestas sólidas.

Realmente me gustó el enfoque de @jessica usando el filtro terms_clauses, porque extiende la función get_terms de una manera muy razonable.

Mi código es una continuación de su idea, con algunos sql de @braydon para reducir duplicados. También permite una variedad de post_types:

/**
 * my_terms_clauses
 *
 * filter the terms clauses
 *
 * @param $clauses array
 * @param $taxonomy string
 * @param $args array
 * @return array
 **/
function my_terms_clauses($clauses, $taxonomy, $args)
{
  global $wpdb;

  if ($args['post_types'])
  {
    $post_types = $args['post_types'];

    // allow for arrays
    if ( is_array($args['post_types']) ) {
      $post_types = implode("','", $args['post_types']);
    }
    $clauses['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";
    $clauses['where'] .= " AND p.post_type IN ('". esc_sql( $post_types ). "') GROUP BY t.term_id";
  }
  return $clauses;
}
add_filter('terms_clauses', 'my_terms_clauses', 99999, 3);

Debido a que get_terms no tiene una cláusula para GROUPY BY, tuve que agregarla al final de la cláusula WHERE. Tenga en cuenta que tengo la prioridad del filtro establecida muy alta, con la esperanza de que siempre sea la última.

Daggerhart
fuente
3

No pude hacer que los argumentos get_terms funcionen con la versión de Gavin del código anterior, pero finalmente lo hice cambiando

$terms2 = get_terms( $taxonomy );

a

$terms2 = get_terms( $taxonomy, $args );

como estaba en la función original de Bainternet.

tzeldin88
fuente
1
Lo arregló en la versión actual
Gavin Hewitt
0

@Bainternet: ¡Gracias! Tuve que modificar ligeramente la función porque no funcionaba (algunos errores tipográficos). El único problema ahora es que el término cuenta está desactivado. El recuento no tiene en cuenta el tipo de publicación, por lo que no creo que pueda usar get_terms () en esto.

function get_terms_by_post_type($post_type,$taxonomy,$fields='all',$args){
    $q_args = array(
        'post_type' => (array)$post_type,
        'posts_per_page' => -1
    );
    $the_query = new WP_Query( $q_args );

    $terms = array();

    while ($the_query->have_posts()) { $the_query->the_post();

        global $post;

        $current_terms = get_the_terms( $post->ID, $taxonomy);

        foreach ($current_terms as $t){
            //avoid duplicates
            if (!in_array($t,$terms)){
                $t->count = 1;
                $terms[] = $t;
            }else{
                $key = array_search($t, $terms);
                $terms[$key]->count = $terms[$key]->count + 1;
            }
        }
    }
    wp_reset_query();

    //return array of term objects
    if ($fields == "all")
        return $terms;
    //return array of term ID's
    if ($fields == "ID"){
        foreach ($terms as $t){
            $re[] = $t->term_id;
        }
        return $re;
    }
    //return array of term names
    if ($fields == "name"){
        foreach ($terms as $t){
            $re[] = $t->name;
        }
        return $re;
    }
    // get terms with get_terms arguments
    if ($fields == "get_terms"){
        $terms2 = get_terms( $taxonomy, $args );

        foreach ($terms as $t){
            if (in_array($t,$terms2)){
                $re[] = $t;
            }
        }
        return $re;
    }
}

EDITAR: Se agregaron las correcciones. Pero de alguna manera todavía no funciona para mí. El recuento aún muestra el valor incorrecto.

Gavin Hewitt
fuente
Esa es una historia diferente, pero puede contar al evitar duplicados en el ciclo while.
Bainternet
Actualicé mi respuesta con una corrección de conteo de términos.
Bainternet
1
No agregue seguimientos como respuestas, a menos que esté respondiendo específicamente a su propia pregunta , en su lugar, deben hacerse adiciones a la pregunta original.
t31os
1
@ t31os: Ah, sí, me preguntaba cómo agregar una adición. No pensé en editar mi pregunta. ¡Gracias!
Gavin Hewitt
¿Cómo puedo llamar a esto? print_r(get_terms_by_post_typea(array('event','category','',array()));este da Warning: Invalid argument supplied for foreach()para la líneaforeach ($current_terms as $t){
devo
0

Evitar duplicados:

//avoid duplicates
    $mivalor=$t->term_id;
    $arr=array_filter($terms, function ($item) use ($mivalor) {return isset($item->term_id) && $item->term_id == $mivalor;});

    if (empty($arr)){
    $t->count=1;
            $terms[] = $t;
        }else{
            $key = array_search($t, $terms);
            $terms[$key]->count = $terms[$key]->count + 1;
        }
Kaotiko
fuente
1
¿Puedes explicar por qué esto resuelve el problema? Ver cómo responder .
brasofilo