Ottenere get_terms per tipo di post personalizzato
Ho due tipi di post personalizzati 'country' e 'city' e una tassonomia condivisa 'flag'.
Se uso:
<?php $flags = get_terms('flag', 'orderby=name&hide_empty=0');
Ottengo un elenco di tutti i termini nella tassonomia, ma voglio limitare l'elenco al tipo di post 'country'.
Come posso farlo?
Usando la nuova soluzione
<?php
$flags = wpse57444_get_terms('flags',array('parent' => 0,'hide_empty' => 1,'post_types' =>array('country')));
foreach ($flags as $flag) {
$childTerms = wpse57444_get_terms('flags',array('parent' => $flag->term_id,'hide_empty' => 1,'post_types' =>array('country')));
foreach ($childTerms as $childTerm) {
echo $childTerm->name.'<br />';
}
}
?>
Non riesco a visualizzare $childTerm->name. Perché?

Temo che questo non sia possibile in modo nativo (almeno per ora?). Vedi questo trac: http://core.trac.wordpress.org/ticket/18106
Allo stesso modo, nella pagina di amministrazione delle tassonomie, il conteggio dei post riflette tutti i tipi di post. (Sono abbastanza sicuro che ci sia anche un ticket trac per questo)
http://core.trac.wordpress.org/ticket/14084
Vedi anche questo post correlato.
Nuova soluzione
Dopo aver scritto quella qui sotto, ho rilasciato un metodo molto migliore (almeno nel senso che puoi fare di più) che consiste nell'utilizzare i filtri forniti nella chiamata get_terms()
. Puoi creare una funzione wrapper che usa get_terms
e (condizionalmente) aggiunge un filtro per manipolare la query SQL (per limitare per tipo di post).
La funzione accetta gli stessi argomenti di get_terms($taxonomies, $args)
. $args
accetta l'argomento aggiuntivo post_types
che prende un array|stringa di tipi di post.
Ma non posso garantire che tutto funzioni "come previsto" (sto pensando al padding del conteggio). Sembra funzionare usando solo gli $args
predefiniti per get_terms
.
function wpse57444_get_terms( $taxonomies, $args=array() ){
// Analizza $args nel caso sia una query string.
$args = wp_parse_args($args);
if( !empty($args['post_types']) ){
$args['post_types'] = (array) $args['post_types'];
add_filter( 'terms_clauses','wpse_filter_terms_by_cpt',10,3);
function wpse_filter_terms_by_cpt( $pieces, $tax, $args){
global $wpdb;
// Non usare il conteggio del database
$pieces['fields'] .=", COUNT(*) " ;
// Unisci tabelle extra per limitare per tipo di post.
$pieces['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 ";
// Limita per tipo di post e raggruppa per term_id per il conteggio.
$post_types_str = implode(',',$args['post_types']);
$pieces['where'].= $wpdb->prepare(" AND p.post_type IN(%s) GROUP BY t.term_id", $post_types_str);
remove_filter( current_filter(), __FUNCTION__ );
return $pieces;
}
} // endif post_types impostato
return get_terms($taxonomies, $args);
}
Utilizzo
$args =array(
'hide_empty' => 0,
'post_types' =>array('country','city'),
);
$terms = wpse57444_get_terms('flag',$args);
Soluzione originale alternativa
Ispirato dal ticket trac sopra citato, (testato e funziona per me)
function wpse57444_filter_terms_by_cpt($taxonomy, $post_types=array() ){
global $wpdb;
$post_types=(array) $post_types;
$key = 'wpse_terms'.md5($taxonomy.serialize($post_types));
$results = wp_cache_get($key);
if ( false === $results ) {
$where =" WHERE 1=1";
if( !empty($post_types) ){
$post_types_str = implode(',',$post_types);
$where.= $wpdb->prepare(" AND p.post_type IN(%s)", $post_types_str);
}
$where .= $wpdb->prepare(" AND tt.taxonomy = %s",$taxonomy);
$query = "
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
GROUP BY t.term_id";
$results = $wpdb->get_results( $query );
wp_cache_set( $key, $results );
}
return $results;
}
Utilizzo
$terms = wpse57444_filter_terms_by_cpt('flag',array('country','city'));
o
$terms = wpse57444_filter_terms_by_cpt('flag','country');

Funziona, ma cosa posso fare con i miei $args? Voglio dire... parent=0&orderby=name&hide_empty=0

no - deve essere un array: $args = array('parent'=>0,'orderby'=>'name','hide_empty'=>0);
. Modificherò questo per consentire stringhe di query...

Dove posso inserire i miei $args in questo esempio: $terms = wpse57444_filter_terms_by_cpt('flag',array('country','city'));
?

Non puoi farlo in quello, solo nella nuova soluzione: wpse57444_get_terms()

@user1443216 $args
è il secondo argomento. Lì devi semplicemente inserire wpse57444_get_terms( 'flag', array( 'country', 'city' ) );

@StephenHarris Anche il core cerca di rimuovere tutto ciò che assomiglia a una query string. Aggiunge solo gonfiore. Ho già rimosso questo tipo di funzionalità da tutto il mio codice dopo una discussione con uno degli sviluppatori del core.

Se uso questo: $flags = wpse57444_get_terms('flags',array('parent' => 0,'hide_empty' => 1,'post_types' =>array('country')));
foreach ($flags as $flag) {
$childTerms = wpse57444_get_terms('flags',array('parent' => $flag->term_id,'hide_empty' => 1,'post_types' =>array('country')));
foreach ($childTerms as $childTerm) {
echo $childTerm->name.'<br />';
}
}
non riesco a fare echo di $childTerm->name

per ottenere un conteggio corretto ho dovuto aggiornare questa riga a:
$pieces['fields'] .= ", COUNT(*) as count ";

La soluzione sopra indicata funziona molto bene con 1 tipo di post. Con molti tipi di post abbiamo riscontrato un problema con prepare. Sembra che non sanifici la $post_types_str
che restituirà IN( 'whitepaper, blog' )
invece di IN( 'whitepaper', 'blog' )
. Si prega di consultare questo articolo per una soluzione alternativa link

Due tipi di post personalizzati 'country' e 'city' e una tassonomia condivisa 'flag'. Vuoi limitare la lista al tipo di post 'country'.
Ecco una soluzione più semplice:
$posts_in_post_type = get_posts( array(
'fields' => 'ids',
'post_type' => 'country',
'posts_per_page' => -1,
) );
$terms = wp_get_object_terms( $posts_in_post_type, 'flag', array( 'ids' ) ); ?>

Sembra essere il metodo consigliato secondo questo ticket trac: https://core.trac.wordpress.org/ticket/18106#comment:7. Nota che questo approccio può essere molto lento, quindi probabilmente dovrai implementare qualche sistema di caching se il tuo sito ha molti articoli.

La risposta di @stephen-harris sopra ha funzionato per me solo parzialmente. Se provavo a usarla due volte sulla pagina, non funzionava. Inoltre, l'idea di seppellire query mysql in quel modo mi preoccupa - penso sia una pratica migliore usare metodi core per ottenere una soluzione, per evitare conflitti con futuri aggiornamenti di WP. Ecco la mia soluzione, basata su un commento #7 sul ticket Trac che ha referenziato
function get_terms_by_custom_post_type( $post_type, $taxonomy ){
$args = array( 'post_type' => $post_type);
$loop = new WP_Query( $args );
$postids = array();
// crea un array di ID dei post
while ( $loop->have_posts() ) : $loop->the_post();
array_push($postids, get_the_ID());
endwhile;
// ottieni i valori della tassonomia basati sull'array di ID
$regions = wp_get_object_terms( $postids, $taxonomy );
return $regions;
}
Utilizzo:
$terms = get_terms_by_custom_post_type('country','flag');
Questo funziona per un solo post type e una sola tassonomia, perché è ciò di cui avevo bisogno, ma non sarebbe troppo difficile modificarlo per accettare valori multipli.
C'era qualche menzione in quel thread Trac che questa soluzione potrebbe non scalare bene, ma sto lavorando su una scala piuttosto piccola e non ho avuto problemi di velocità.

questa soluzione mi sembra più "nativa" - comunque ->
dovresti chiamare "wp_reset_postdata()" subito dopo l'"endwhile" del loop: https://wordpress.stackexchange.com/questions/144343/wp-reset-postdata-or-wp-reset-query-after-a-custom-loop

[modifica] Questo è un commento all'eccellente risposta di Stephen Harris.
Non restituisce alcun termine se utilizzato con più tipi di post come questo $flags = wpse57444_get_terms('flags', array('post_types' => array('country','city')));
. Questo accade perché $wpdb->prepare sanifica la stringa $post_types_str in p.post_type IN('country,city')
mentre dovrebbe essere p.post_type IN('country','city')
. Vedi questo ticket: 11102. Usa la soluzione da questo argomento per risolvere il problema: https://stackoverflow.com/a/10634225

Ho anche provato a utilizzare la risposta di @Stephen Harris, ma la query di cui avevo bisogno era piuttosto complessa da scrivere come singola query e utilizzando i filtri.
Inoltre, avevo anche bisogno di usare quella funzione più volte nella stessa pagina e ho risolto il problema dichiarando la funzione wpse_filter_terms_by_cpt
al di fuori della funzione wrapper.
Comunque, secondo me la risposta di @Mark Pruce è più adatta, per le stesse ragioni che ha citato, anche se richiede di fare un'ulteriore query (e il relativo loop) per preparare gli argomenti per la funzione wp_get_object_terms
.
