ottenere tutti i valori per una chiave di campo personalizzato (cross-post)
So come ottenere il valore di un campo personalizzato per un post specifico.
get_post_meta($post_id, $key, $single);
Quello che mi serve è ottenere tutti i valori associati a una specifica chiave di campo personalizzato, attraverso tutti i post.
Qualcuno conosce un modo efficiente per farlo? Non vorrei dover ciclare attraverso tutti gli ID dei post nel database.
Esempio:
4 post tutti con valori diversi per un campo personalizzato chiamato 'Mood'. 2 post hanno il valore 'felice', 1 post ha 'arrabbiato' e 1 post ha 'triste'
Voglio ottenere come output: attraverso tutti i post abbiamo: due autori felici, uno arrabbiato e uno triste.
Ma per MOLTI post.
Quello che sto cercando è:
- una funzione WP per ottenere questo. oppure
- una query personalizzata per ottenere questo nel modo più efficiente possibile.

Un approccio possibile sarebbe utilizzare uno dei metodi helper nella classe WPDB per eseguire una query più raffinata basata sui meta.
Utilizzando la funzione $wpdb
get_col
è possibile ottenere un semplice array piatto di dati.
Ecco una funzione di esempio che interroga il database per tutti i post di un determinato tipo di post, stato del post e chiave meta (o campo personalizzato per chi è meno tecnico).
function get_meta_values( $meta_key = '', $post_type = 'post', $post_status = 'publish' ) {
global $wpdb;
if( empty( $meta_key ) )
return;
$meta_values = $wpdb->get_col( $wpdb->prepare( "
SELECT pm.meta_value FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = %s
AND p.post_type = %s
AND p.post_status = %s
", $meta_key, $post_type, $post_status ) );
return $meta_values;
}
Quindi, ad esempio, se vuoi scoprire quali post hanno una chiave meta rating, per il tipo di post movies e memorizzare queste informazioni in una variabile, un esempio di chiamata sarebbe:
$movie_ratings = get_meta_values( 'rating', 'movies' );
Se vuoi fare qualcosa in più che stampare semplicemente quei dati a schermo, la funzione PHP implode
può condensare rapidamente l'array in una stringa.
// Stampa i valori meta separati da un'interruzione di riga
echo implode( '<br />', get_meta_values( 'YOURKEY' ));
Puoi anche utilizzare i dati restituiti per calcolare quanti post hanno i valori meta eseguendo una semplice iterazione (ciclo) sui dati restituiti e costruendo un array dei conteggi, ad esempio:
$movie_ratings = get_meta_values( 'rating', 'movies' );
if( !empty( $movie_ratings ) ) {
$num_of_ratings = [];
foreach( $movie_ratings as $meta_value ) {
$num_of_ratings[$meta_value] = isset( $num_of_ratings[$meta_value] ) ? $num_of_ratings[$meta_value] + 1 : 1;
}
}
// Output del numero di valutazioni
printf( '<pre>%s</pre>', print_r( $num_of_ratings ) );
/*
Output:
Array(
[5] => 10
[9] => 2
)
es. ci sono 10 post di film con valutazione 5 e 2 post di film con valutazione 9.
*/
Questa logica potrebbe essere applicata a vari tipi di dati e estesa per funzionare in molti modi diversi. Spero che i miei esempi siano stati utili e abbastanza semplici da seguire.
Utilizzo dei transient per memorizzare nella cache i risultati
Ed ecco una versione aggiornata che utilizza i transient di WordPress per memorizzare nella cache la query, poiché questo sembra essere la principale critica all'uso di $wpdb
nelle altre risposte fornite.
function get_meta_values( string $meta_key, string $post_type = 'post', bool $distinct = false, string $post_status = 'publish' ) {
global $wpdb, $wp_post_types;
if( !isset( $wp_post_types[$post_type] ) )
// Stringa WP esistente, dovrebbe essere tradotta così com'è
return __( 'Invalid post type.' );
$transient_key = 'get_' . $wp_post_types[$post_type]->name . '_type_meta_values';
$get_meta_values = get_transient( $transient_key );
if( true === (bool)$get_meta_values )
return $get_meta_values;
$distinct = $distinct ? ' DISTINCT' : '';
$get_meta_values = $wpdb->get_col( $wpdb->prepare( "
SELECT{$distinct} pm.meta_value FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = %s
AND p.post_type = %s
AND p.post_status = %s
", $meta_key, $post_type, $post_status ) );
set_transient( $transient_key, $get_meta_values, DAY_IN_SECONDS );
return $get_meta_values;
}
La costante DAY_IN_SECONDS
è una delle varie costanti di tempo in secondi configurate da WordPress.
Ho aggiornato i nomi degli argomenti e delle variabili per renderli più coerenti con la denominazione di WordPress e ho anche implementato DISTINCT
come parametro opzionale seguendo il suggerimento di Howdy_McGee nei commenti.

Anche una curiosità per i futuri lettori: se vuoi estrarre solo valori meta unici, devi scrivere DISTINCT
subito dopo il SELECT
nella funzione sopra. Potrebbe essere utile.

Non è buona pratica né necessario utilizzare la variabile globale $wpdb:
// funzione per recuperare tutti i valori meta possibili della chiave meta specificata.
function get_meta_values( $meta_key, $post_type = 'post' ) {
$posts = get_posts(
array(
'post_type' => $post_type,
'meta_key' => $meta_key,
'posts_per_page' => -1,
)
);
$meta_values = array();
foreach( $posts as $post ) {
$meta_values[] = get_post_meta( $post->ID, $meta_key, true );
}
return $meta_values;
}
$meta_values = get_meta_values( $meta_key, $post_type );

Nella maggior parte dei casi, questo sarebbe il mio metodo preferito per farlo. Esegue cinque query invece di una sola, ma poiché utilizza le procedure standard di WordPress per generarle e inviarle, verrà attivata qualsiasi cache specifica della piattaforma (come la Object Caching di WP Engine o qualche plugin casuale). I dati verranno anche memorizzati nella cache interna di WordPress per la durata della richiesta, quindi non sarà necessario recuperarli nuovamente dal database, se necessario.

Inoltre, eventuali filtri verranno applicati ai dati, il che potrebbe essere estremamente importante su, ad esempio, un sito multilingue. Infine, poiché utilizza solo le funzioni standard del core di WordPress, è molto meno probabile che venga interrotto da un aggiornamento futuro.

Potrebbe essere reso più performante limitando la query agli ID dei post?
Aggiungi: 'fields' => 'ids'
Quindi, l'array della query sarebbe:
array(
'post_type' => $post_type,
'meta_key' => $meta_key,
'posts_per_page' => -1,
'fields' => 'ids'
)

Posso immaginare casi in cui sarebbe valido avere più meta valori dello stesso valore, e quindi non ho aggiunto questa modifica al mio codice. Se vuoi valori distinti, questo sarebbe il modo di procedere. Inoltre potresti anche aggiungerlo come argomento della funzione (in modo da poterlo usare o meno, a seconda dei casi).

Per ottenere tutti i valori meta da una chiave meta
Consulta wp->db wordpress codex
$values = $wpdb->get_col("SELECT meta_value
FROM $wpdb->postmeta WHERE meta_key = 'yourmetakey'" );

Il problema con questo approccio è la mancanza di specificità, otterrai numerosi risultati da una query di questo tipo, che potrebbero includere bozze, elementi cestinati, articoli, pagine e qualsiasi altro tipo di post esistente. Non dovresti mai interrogare ciò di cui non hai bisogno, la specificità è assolutamente necessaria in questo caso.

Sebbene sia vero che potresti ottenere valori da altri tipi di post e stati, ci sono momenti in cui tutto ciò di cui hai bisogno sono i valori e non hai utilizzato quella meta_key da nessuna parte tranne dove ti serve. Se tutti/la maggior parte dei valori sono unici, questa potrebbe essere la soluzione migliore.

il modo più veloce sarebbe una query SQL personalizzata e non sono sicuro ma puoi provare
$wpdb->get_results("
SELECT posts.* , COUNT(*) 'moodcount'
FROM $wpdb->posts as posts
JOIN $wpdb->postmeta as postmeta
ON postmeta.post_id = posts.ID
AND postmeta.meta_key = 'Mood'
GROUP BY postmeta.meta_key
");
Se funziona è un buon punto di partenza.

grazie, ma le query personalizzate non dovrebbero essere evitate "a tutti i costi"? Preferirei usare lo strato di astrazione di WP (è così che si chiama?)... ma ovviamente se non è possibile..

Le query personalizzate, se scritte nel modo giusto, possono essere migliori e dovresti evitarle solo se non sai cosa stai facendo.

Non c'è motivo per cui non si possa combinare il codice di t31os e Bainternet per ottenere un'istruzione preparata riutilizzabile (in stile WordPress) che restituisca sia il conteggio che i valori in un'unica operazione efficiente.
È una query personalizzata ma utilizza comunque il livello di astrazione del database di WordPress - quindi, ad esempio, non importa quali siano i nomi reali delle tabelle o se cambiano, ed essendo un'istruzione preparata siamo molto più protetti da attacchi SQL injection, ecc.
In questo caso specifico non sto più verificando il tipo di post ed escludo le stringhe vuote:
$r = $wpdb->get_results( $wpdb->prepare( "
SELECT pm.meta_value AS name, count(*) AS count FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = '%s'
AND pm.meta_value != ''
AND p.post_type = '%s'
GROUP BY pm.meta_value
ORDER BY pm.meta_value
", $key, $type)
);
return $r;
In questo caso particolare restituirà un array di oggetti come questo:
array
0 =>
object(stdClass)[359]
public 'name' => string 'Hamish' (length=6)
public 'count' => string '3' (length=1)
1 =>
object(stdClass)[360]
public 'name' => string 'Ida' (length=11)
public 'count' => string '1' (length=1)
2 =>
object(stdClass)[361]
public 'name' => string 'John' (length=12)
public 'count' => string '1' (length=1)

Nota che questo valore predefinito è il post corrente, quando non viene specificato alcun post_id.

Questo restituisce semplicemente tutti i campi personalizzati per un singolo post, che per impostazione predefinita è quello con ID "0". Nota che la documentazione menziona esplicitamente che "i parametri non devono essere considerati opzionali".
