Ottieni termini per tassonomia E post_type


17

Ho 2 tipi di post personalizzati "segnalibri" e "snippet" e un "tag" di tassonomia condivisa. Posso generare un elenco di tutti i termini nella tassonomia con get_terms (), ma non riesco a capire come limitare l'elenco al tipo di post. Quello che sto sostanzialmente cercando è qualcosa del genere:

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

C'è un modo per raggiungere questo obiettivo? Le idee sono molto apprezzate !!

Oh, sono su WP 3.1.1

Risposte:


11

Ecco un altro modo per fare qualcosa di simile, con una query 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;

}

Sì! Questo fa esattamente quello che voglio.
Gavin Hewitt,

print_r(get_terms_by_post_type(array('category') , array('event') ));spettacoliWarning: Missing argument 2 for wpdb::prepare()
devo

Potrei sbagliarmi, ma dalla parte superiore della mia testa, non penso che quelle dichiarazioni 'join' funzioneranno - cioè, funzionerebbero solo se passassero array a valore singolo. Questo perché la funzione di preparazione sfuggirebbe a tutte le virgolette singole generate e considererebbe ogni stringa "join" intera.
Codesmith

14

Quindi succede semplicemente che avevo bisogno di qualcosa del genere per un progetto a cui sto lavorando. Ho semplicemente scritto una query per selezionare tutti i post di un tipo personalizzato, quindi controllo quali sono i termini effettivi della mia tassonomia che stanno utilizzando.

Poi ho ottenuto tutti i termini di quella tassonomia usando get_terms()e poi ho usato solo quelli che erano in entrambe le liste, l'ho concluso in una funzione e ho finito.

Ma poi avevo bisogno di qualcosa di più dei soli ID: avevo bisogno dei nomi, quindi ho aggiunto un nuovo argomento chiamato $fieldscosì da poter dire alla funzione cosa restituire. Quindi ho pensato che get_termsaccetta molti argomenti e la mia funzione era limitata a semplici termini che vengono utilizzati da un tipo di post, quindi ho aggiunto un'altra ifistruzione e il gioco è fatto:

La funzione:

/* 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 hai solo bisogno di un elenco di id di termine, allora:

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

Se hai solo bisogno di un elenco di nomi di termini, allora:

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

Se hai solo bisogno di un elenco di oggetti termine, allora:

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

E se hai bisogno di usare argomenti extra di get_terms come: orderby, order, gerarchical ...

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

Godere!

Aggiornare:

Per correggere il conteggio dei termini per una specifica modifica del tipo di post:

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

per:

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

non sarebbe meglio se usi (array) $argsinvece di un elenco di 4 $ var? Ciò ti permetterebbe di non preoccuparti dell'ordine che inserisci negli argomenti, quindi qualcosa di simile get_terms_by_post_type( $args = array( 'taxonomies', 'args', 'post_type', 'fields' => 'all') )e poi chiamali all'interno della funzione con $args['taxonomies']. Ciò ti aiuterebbe a evitare di aggiungere valori vuoti e di dover ricordare l'ordine dei tuoi argomenti. Suggerirei anche di usare virgolette singole anziché doppie. Li ho visti essere cinque volte più veloci.
Kaiser

1
@kaiser - Le stringhe tra virgolette doppie devono essere analizzate, in quanto i valori tra virgolette singole vengono sempre trattati come letterali. Quando usi variabili in una stringa ha senso ed è perfettamente corretto usare virgolette doppie, ma per valori di stringa non variabili le virgolette singole sono più ideali (perché non dovranno essere analizzate) e leggermente più veloci (noi " parlando di millisecondi nella maggior parte dei casi).
t31os,

@ t31os - Assolutamente corretto. Io preferisco ancora 'this is my mood: '.$valueoltre "this is my mood: $value", a causa della leggibilità. Quando si tratta di velocità: non è leggermente - ho misurato fino a cinque volte. E quando usi le virgolette doppie in tutto il tuo tema ovunque, queste si sommano rapidamente quando ricevi molte richieste. Comunque bravo l'hai chiarito.
Kaiser

@ t31os Da una discussione ho misurato nuovamente la velocità di "vs. 'e ho sbagliato. La differenza è molto al di fuori di tutto ciò che qualcuno noterebbe.
Kaiser

1
+1 bella funzione! 2 errori di battitura: $ taxonomies viene utilizzato nella funzione $ taxonomy e $ terms [] = $ c; deve essere $ terms [] = $ t;
Rob Vermeer,

8

Ho scritto una funzione che ti consente di passare post_typel' $argsarray alla get_terms()funzione:

Da HT a @braydon per la scrittura di 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);

7

Ottima domanda e risposte solide.

Mi è piaciuto molto l'approccio di @jessica usando il filtro terms_clauses, perché estende la funzione get_terms in un modo molto ragionevole.

Il mio codice è una continuazione della sua idea, con alcuni sql di @braydon per ridurre i duplicati. Permette anche una matrice di 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);

Poiché get_terms non ha una clausola per GROUPY BY, ho dovuto aggiungerlo alla fine della clausola WHERE. Si noti che ho impostato la priorità del filtro molto alta, nella speranza che vada sempre per ultimo.


3

Non sono stato in grado di fare in modo che gli argomenti get_terms funzionassero con la versione di Gavin del codice sopra, ma alla fine l'ho fatto cambiando

$terms2 = get_terms( $taxonomy );

per

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

come era nella funzione originale di Bainternet.


1
Risolto il problema con la versione corrente
Gavin Hewitt,

0

@Bainternet: grazie! Ho dovuto modificare leggermente la funzione perché non funzionava (alcuni errori di battitura). L'unico problema ora è che il conteggio dei termini è disattivato. Il conteggio non sta prendendo in considerazione il tipo di post, quindi non penso che tu possa usare get_terms () in questo.

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: aggiunte le correzioni. Ma in qualche modo non funziona ancora per me. Il conteggio mostra ancora il valore errato.


È una storia diversa, ma puoi contare quando eviti i duplicati nel ciclo while.
Bainternet,

Ho aggiornato la mia risposta con una correzione del conteggio dei termini.
Bainternet,

1
Si prega di non aggiungere follow-up come risposte, a meno che non si stia rispondendo in modo specifico alla propria domanda , le aggiunte dovrebbero invece essere fatte alla domanda originale.
t31os,

1
@ t31os: Ah sì, mi chiedevo come aggiungere un'aggiunta. Non ho pensato di modificare la mia domanda. Grazie!
Gavin Hewitt,

Come posso chiamare questo? print_r(get_terms_by_post_typea(array('event','category','',array()));questo dà Warning: Invalid argument supplied for foreach()per la lineaforeach ($current_terms as $t){
devo

0

Evita duplicati:

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

1
Puoi spiegare perché questo risolve il problema? Vedi come rispondere .
brasofilo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.