È possibile ottenere get_terms per tassonomia E post_type?

9 apr 2011, 03:38:03
Visualizzazioni: 27.8K
Voti: 18

Ho 2 tipi di post personalizzati 'bookmarks' e 'snippets' e una tassonomia condivisa 'tag'. 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 cercando è sostanzialmente qualcosa del genere:

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

C'è un modo per ottenere questo risultato? Ogni idea è apprezzata!

Ah, sto usando WP 3.1.1

0
Tutte le risposte alla domanda 7
5
14

Mi è capitato di aver bisogno di qualcosa del genere per un progetto su cui sto lavorando. Ho semplicemente scritto una query per selezionare tutti i post di un tipo personalizzato, poi ho verificato quali sono i termini effettivi della mia tassonomia che stanno utilizzando.

Poi ho ottenuto tutti i termini di quella tassonomia usando get_terms() e ho utilizzato solo quelli presenti in entrambe le liste, ho racchiuso tutto in una funzione e ho finito.

Ma poi avevo bisogno di più che solo gli ID: mi servivano i nomi, quindi ho aggiunto un nuovo argomento chiamato $fields per poter specificare alla funzione cosa restituire. Poi ho pensato che get_terms accetta molti argomenti e la mia funzione era limitata solo ai termini utilizzati da un tipo di post, quindi ho aggiunto un ulteriore if ed ecco qui:

La Funzione:

/* ottiene i termini limitati a un tipo di post 
 @ $taxonomies - (string|array) (obbligatorio) Le tassonomie da cui recuperare i termini. 
 @ $args  -  (string|array) tutti i possibili argomenti di get_terms http://codex.wordpress.org/Function_Reference/get_terms
 @ $post_type - (string|array) i tipi di post a cui limitare i termini
 @ $fields - (string) Cosa restituire (predefinito: all) accetta ID,name,all,get_terms. 
 se vuoi usare gli argomenti di get_terms allora $fields deve essere impostato su '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){
          //evita duplicati
            if (!in_array($t,$terms)){
                $terms[] = $c;
            }
        }
    }
    wp_reset_query();
    //restituisce un array di oggetti termine
    if ($fields == "all")
        return $terms;
    //restituisce un array di ID dei termini
    if ($fields == "ID"){
        foreach ($terms as $t){
            $re[] = $t->term_id;
        }
        return $re;
    }
    //restituisce un array di nomi dei termini
    if ($fields == "name"){
        foreach ($terms as $t){
            $re[] = $t->name;
        }
        return $re;
    }
    // ottiene i termini con gli argomenti di get_terms
    if ($fields == "get_terms"){
        $terms2 = get_terms( $taxonomies, $args );
        foreach ($terms as $t){
            if (in_array($t,$terms2)){
                $re[] = $t;
            }
        }
        return $re;
    }
}

Utilizzo:

Se hai bisogno solo di una lista di ID dei termini allora:

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

Se hai bisogno solo di una lista di nomi dei termini allora:

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

Se hai bisogno solo di una lista di oggetti termine allora:

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

E se hai bisogno di usare argomenti aggiuntivi di get_terms come: orderby, order, hierarchical ...

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

Divertiti!

Aggiornamento:

Per correggere il conteggio dei termini per un tipo di post specifico cambia:

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

in:

foreach ($current_terms as $t){
    //evita duplicati
    if (!in_array($t,$terms)){
        $t->count = 1;
        $terms[] = $t;
    }else{
        $key = array_search($t, $terms);
        $terms[$key]->count = $terms[$key]->count + 1;
    }
}
9 apr 2011 04:21:43
Commenti

non sarebbe meglio se usassi (array) $args invece di una lista di 4 $vars? Questo ti permetterebbe di non preoccuparti dell'ordine in cui inserisci gli argomenti, quindi qualcosa come get_terms_by_post_type( $args = array( 'taxonomies', 'args', 'post_type', 'fields' => 'all') ) e poi richiamarli all'interno della funzione con $args['taxonomies']. Questo ti aiuterebbe a evitare di aggiungere valori vuoti e dover ricordare l'ordine dei tuoi argomenti. Suggerirei anche di usare apici singoli invece di doppi. Ho visto che possono essere fino a cinque volte più veloci.

kaiser kaiser
9 apr 2011 13:48:50

@kaiser - Le stringhe tra virgolette doppie devono essere analizzate, mentre i valori tra apici singoli sono sempre trattati come letterali. Quando usi variabili in una stringa ha senso ed è perfettamente accettabile usare le virgolette doppie, ma per valori di stringa senza variabili gli apici singoli sono più ideali (perché non devono essere analizzati) e leggermente più veloci (parliamo di millisecondi nella maggior parte dei casi).

t31os t31os
9 apr 2011 14:35:20

@t31os - Assolutamente corretto. Preferisco ancora 'questo è il mio umore: '.$value rispetto a "questo è il mio umore: $value", per una questione di leggibilità. Quando si tratta di velocità: non è leggermente - ho misurato fino a cinque volte. E quando usi virgolette doppie ovunque nel tuo tema, si sommeranno rapidamente quando hai molte richieste. Comunque bene che tu l'abbia chiarito.

kaiser kaiser
9 apr 2011 15:25:26

@t31os Da una discussione ho rimisurato la velocità di " rispetto a ' e mi sbagliavo. La differenza è ben al di là di qualsiasi cosa che qualcuno potrebbe notare.

kaiser kaiser
24 lug 2011 18:24:17

+1 bella funzione! 2 errori di battitura: $taxonomies viene usato nella funzione come $taxonomy e $terms[] = $c; dovrebbe essere $terms[] = $t;

Rob Vermeer Rob Vermeer
5 feb 2012 19:45:27
3
13

Ottima domanda e risposte solide.

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

Il mio codice è una continuazione della sua idea, con un po' di SQL preso da @braydon per ridurre i duplicati. Consente anche di utilizzare un array di post_types:

/**
 * my_terms_clauses
 *
 * filtra le clausole dei termini
 *
 * @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 = implode("','", array_map('esc_sql', (array) $args['post_types']));

    // consente gli array
    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 ('". $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 GROUP BY, ho dovuto aggiungerla alla fine della clausola WHERE. Nota che ho impostato la priorità del filtro molto alta, sperando che venga sempre eseguito per ultimo.

4 apr 2015 06:09:25
Commenti

Dovresti sostituire $post_types = $args['post_types']; con $post_types = implode("','", array_map('esc_sql', (array) $args['post_types'])); e rimuovere esc_sql() dalla clausola IN altrimenti questa clausola finirà con \' tra i post types quando vengono forniti più post types in $args['post_types']

ZalemCitizen ZalemCitizen
28 set 2020 10:58:33

L'unico che mi ha permesso di usare get_terms e ha funzionato per me nel 2022

LBF LBF
28 ott 2022 00:03:13

L'argomento hide_empty verrà ancora applicato a tutti i post, non solo al post type selezionato?

Adrian B Adrian B
4 ott 2023 00:43:15
3
12

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

}
26 lug 2011 11:40:10
Commenti

Sì! Fa esattamente quello che voglio.

Gavin Hewitt Gavin Hewitt
7 nov 2011 03:33:30

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

Anshad Vattapoyil Anshad Vattapoyil
20 giu 2014 05:20:40

Potrei sbagliarmi, ma a prima vista non credo che quelle dichiarazioni 'join' funzioneranno - cioè, funzionerebbero solo se passate array a valore singolo. Questo perché la funzione prepare eseguirebbe l'escape di tutti gli apici singoli generati e considererebbe ogni intera 'join' come una stringa.

Codesmith Codesmith
12 mar 2019 19:19:43
0
10

Ho scritto una funzione che permette di passare post_type nell'array $args alla funzione get_terms():

Un ringraziamento speciale a @braydon per aver scritto l'SQL.

 /**
 * terms_clauses
 *
 * Filtra le clausole dei termini
 *
 * @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);
8 giu 2012 08:14:39
1

Non sono riuscito a far funzionare gli argomenti di get_terms con la versione del codice di Gavin sopra riportata, ma alla fine ci sono riuscito cambiando

$terms2 = get_terms( $taxonomy );

in

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

come era nella funzione originale di Bainternet.

10 apr 2011 05:16:13
Commenti

Risolto nella versione corrente

Gavin Hewitt Gavin Hewitt
10 apr 2011 14:06:01
5

@Bainternet: Grazie! Ho dovuto modificare leggermente la funzione perché non funzionava (alcuni errori di battitura). L'unico problema ora è che il conteggio dei termini non è corretto. Il conteggio non tiene conto del tipo di post, quindi penso che non si possa usare get_terms() in questo caso.

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){
            //evita duplicati
            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();

    //ritorna un array di oggetti term
    if ($fields == "all")
        return $terms;
    //ritorna un array di ID dei termini
    if ($fields == "ID"){
        foreach ($terms as $t){
            $re[] = $t->term_id;
        }
        return $re;
    }
    //ritorna un array di nomi dei termini
    if ($fields == "name"){
        foreach ($terms as $t){
            $re[] = $t->name;
        }
        return $re;
    }
    // ottieni i termini con gli argomenti di get_terms
    if ($fields == "get_terms"){
        $terms2 = get_terms( $taxonomy, $args );

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

MODIFICA: Aggiunte le correzioni. Ma in qualche modo ancora non funziona per me. Il conteggio mostra ancora il valore errato.

9 apr 2011 14:45:03
Commenti

Questa è un'altra storia, ma puoi conteggiare quando eviti i duplicati nel ciclo while.

Bainternet Bainternet
9 apr 2011 16:34:55

Ho aggiornato la mia risposta con una correzione per il conteggio dei termini.

Bainternet Bainternet
9 apr 2011 17:25:26

Per favore non aggiungere follow-up come risposte, a meno che tu non stia specificamente rispondendo alla tua domanda. Le aggiunte dovrebbero invece essere fatte alla domanda originale.

t31os t31os
10 apr 2011 13:06:44

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

Gavin Hewitt Gavin Hewitt
10 apr 2011 13:58:36

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 riga foreach ($current_terms as $t){

Anshad Vattapoyil Anshad Vattapoyil
20 giu 2014 05:32:21
1

Evita duplicati:

//evita duplicati
    $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;
        }
26 feb 2013 19:10:19
Commenti

Puoi spiegare perché questo risolve il problema? Vedi [risposta].

brasofilo brasofilo
26 feb 2013 19:38:46