Obter termos por taxonomy AND post_type

16

Eu tenho dois tipos de marcadores de postagem personalizados e "snippets" e uma "tag" de taxonomia compartilhada. Eu posso gerar uma lista de todos os termos na taxonomia com get_terms (), mas não consigo descobrir como limitar a lista ao tipo de postagem. O que eu estou procurando basicamente é algo assim:

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

Existe uma maneira de conseguir isso? Ideias são muito apreciadas !!

Ah, eu estou no WP 3.1.1

    
por Gavin Hewitt 09.04.2011 / 02:38
fonte

7 respostas

11

Aqui está outra maneira de fazer algo semelhante, com uma 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;

}
    
por Braydon 26.07.2011 / 10:40
fonte
14

Então, acontece que eu precisava de algo assim para um projeto em que estou trabalhando. Eu simplesmente escrevi uma consulta para selecionar todas as postagens de um tipo personalizado e, em seguida, verifiquei quais são os termos reais da minha taxonomia que eles estão usando.

Depois, eu obtive todos os termos dessa taxonomia usando get_terms() e, em seguida, usei apenas os que estavam em ambas as listas, agrupei-os em uma função e terminei.

Mas então eu precisei de mais do que apenas os ID's: Eu precisava dos nomes, então eu adicionei um novo argumento chamado $fields para que eu pudesse dizer à função o que devolver. Então eu percebi que get_terms aceita muitos argumentos e minha função foi limitada a simplesmente termos que estão sendo usados por um tipo de post então eu adicionei mais uma declaração if e lá você vai:

A função:

/* 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:

Se você precisar apenas de uma lista de ids de termo, então:

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

Se você precisar apenas de uma lista de nomes de termos, então:

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

Se você precisar apenas de uma lista de objetos de termo, então:

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

E se você precisar usar argumentos extras de get_terms como: orderby, order, hierarchical ...

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

Aproveite!

Atualização:

Para corrigir a contagem de termos para uma mudança de tipo de postagem específica:

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

para:

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;
    }
}
    
por Bainternet 09.04.2011 / 03:21
fonte
8

Eu escrevi uma função que permite que você passe post_type na matriz $args para a função get_terms() :

HT a @braydon por escrever o 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);
    
por jessica 08.06.2012 / 07:14
fonte
7

Grande pergunta e respostas sólidas.

Eu realmente gostei da abordagem de @jessica usando o filtro terms_clauses, porque ele estende a função get_terms de uma maneira bastante razoável.

Meu código é uma continuação da idéia dela, com algum sql do @braydon para reduzir duplicatas. Também permite uma matriz 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);

Como get_terms não tem uma cláusula para GROUPY BY, tive que adicioná-lo ao final da cláusula WHERE. Observe que eu tenho a prioridade do filtro configurada muito alta, na esperança de que sempre vá durar.

    
por daggerhart 04.04.2015 / 05:09
fonte
3

Não consegui fazer os argumentos get_terms para trabalhar com a versão do código acima, mas finalmente consegui alterar

$terms2 = get_terms( $taxonomy );

para

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

como estava na função original de Bainternet.

    
por tzeldin88 10.04.2011 / 04:16
fonte
0

@Bainternet: Obrigado! Eu tive que alterar a função um pouco porque não estava funcionando (alguns erros de digitação). O único problema agora é que a contagem de termos está desativada. A contagem não está levando o tipo de postagem em consideração, então acho que você não pode usar get_terms () nisso.

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

EDIT: Adicionado a (s) correção (ões). Mas de alguma forma ainda não está funcionando para mim. A contagem ainda mostra o valor incorreto.

    
por Gavin Hewitt 09.04.2011 / 13:45
fonte
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;
        }
    
por Kaotiko 26.02.2013 / 18:10
fonte