Obter termos por taxonomia E post_type

17

Eu tenho dois tipos de postagem personalizados 'bookmarks' e 'snippets' e uma taxonomia compartilhada 'tag'. 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 como isto:

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

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

Ah, eu estou no WP 3.1.1

Gavin Hewitt
fonte

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;

}
Braydon
fonte
Sim! Isso faz exatamente o que eu quero.
Gavin Hewitt
print_r(get_terms_by_post_type(array('category') , array('event') ));mostraWarning: Missing argument 2 for wpdb::prepare()
devo
Eu posso estar errado, mas, pensando bem, não acho que essas declarações de 'junção' funcionem - ou seja, elas só funcionariam se fossem aprovadas matrizes de valor único. Isso ocorre porque a função de preparação escaparia de todas as aspas simples geradas e consideraria cada sequência de caracteres 'unida'.
Codesmith 12/03
14

Acontece que eu precisava de algo parecido para um projeto em que estou trabalhando. Eu simplesmente escrevi uma consulta para selecionar todas as postagens de um tipo personalizado e depois verifico quais são os termos reais da minha taxonomia que eles estão usando.

Então eu peguei todos os termos dessa taxonomia get_terms()e depois usei apenas aqueles que estavam nas duas listas, envolvi-os em uma função e acabei.

Mas então eu precisava de mais do que apenas os IDs: precisava dos nomes, então adicionei um novo argumento nomeado $fieldspara poder dizer à função o que retornar. Então imaginei que get_termsaceita muitos argumentos e minha função era limitada a simplesmente termos que estão sendo usados ​​por um tipo de post, então adicionei mais uma ifinstrução e pronto:

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 termos, 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 termos, 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');

Desfrutar!

Atualizar:

Para corrigir a contagem de termos para uma alteração específica do tipo de postagem:

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;
    }
}
Bainternet
fonte
não seria melhor se você usasse em (array) $argsvez de uma lista de 4 $ vars? Isso permitiria que você não se importasse com a ordem em que lançou os argumentos, então algo como get_terms_by_post_type( $args = array( 'taxonomies', 'args', 'post_type', 'fields' => 'all') )e os chame dentro da função with $args['taxonomies']. Isso ajudaria você a não adicionar valores vazios e a lembrar a ordem dos seus argumentos. Eu também sugeriria usar aspas simples em vez de duplas. Eu os vi sendo até cinco vezes mais rápidos.
kaiser
1
@ kaiser - Seqüências de caracteres entre aspas duplas devem ser analisadas, onde valores entre aspas simples são sempre tratados como literais. Quando você usa variáveis ​​em uma string, faz sentido e é perfeitamente bom usar aspas duplas, mas para valores de string não variáveis ​​aspas simples são mais ideais (porque elas não precisam ser analisadas) e um pouco mais rápidas (nós ' estamos falando de milissegundos na maioria dos casos).
T31os 09/04
@ t31os - Absolutamente correto. Eu ainda prefiro 'this is my mood: '.$valuemais "this is my mood: $value", por causa da legibilidade. Quando se trata de velocidade: não é um pouco - eu medi até cinco vezes. E quando você usa aspas duplas em todo o tema em todos os lugares, elas são resumidas rapidamente quando você recebe muitos pedidos. Seja como for, você deixou isso claro.
kaiser
@ t31os Fora de uma discussão, medi novamente a velocidade de "vs. 'e estava errado. A diferença está longe de qualquer coisa que alguém possa notar.
Kaiser
1
+1 boa função! 2 erros de digitação: $ taxonomies é usado na função $ taxonomy e $ terms [] = $ c; tem que ser $ terms [] = $ t;
22630 Robertmeme
8

Eu escrevi uma função que permite passar post_typeo $argsarray para a get_terms()função:

HT para @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);
jessica
fonte
7

Ótima pergunta e respostas sólidas.

Gostei muito da abordagem do @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 ideia 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 possui uma cláusula para GROUPY BY, tive que adicioná-la ao final da cláusula WHERE. Observe que eu tenho a prioridade do filtro muito alta, na esperança de que ela sempre dure.

Daggerhart
fonte
3

Não consegui fazer com que os argumentos get_terms funcionassem com a versão do código de Gavin acima, mas finalmente fiz alterando

$terms2 = get_terms( $taxonomy );

para

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

como estava na função original de Bainternet.

tzeldin88
fonte
1
Corrigido na versão atual
Gavin Hewitt
0

@Bainternet: Obrigado! Eu tive que alterar ligeiramente a função 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 em consideração o tipo de postagem, 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: Adicionadas as correções. Mas de alguma forma ainda não está funcionando para mim. A contagem ainda mostra o valor incorreto.

Gavin Hewitt
fonte
Essa é uma história diferente, mas você pode contar ao evitar duplicatas no loop while.
Bainternet
Atualizei minha resposta com uma correção de contagem de termos.
Bainternet
1
Não adicione acompanhamentos como respostas, a menos que você esteja respondendo especificamente à sua própria pergunta , as adições devem ser feitas na pergunta original.
T31os
1
@ t31os: Ah, sim, eu queria saber como adicionar uma adição. Não pensei em editar minha pergunta. Obrigado!
Gavin Hewitt
Como posso chamar isso? print_r(get_terms_by_post_typea(array('event','category','',array()));este dá Warning: Invalid argument supplied for foreach()para a linhaforeach ($current_terms as $t){
devo
0

Evite duplicatas:

//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
fonte
1
Você pode explicar por que isso resolve o problema? Veja Como responder .
26133 brasofilo