È possibile ottenere get_terms per tassonomia E post_type?
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

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

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 - 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 - 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.

@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.

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.

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']

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

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

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

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.

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

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.

@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.

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

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

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: Ah sì, mi chiedevo come aggiungere un'aggiunta. Non avevo pensato di modificare la mia domanda. Grazie!

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