WP_Query può restituire i meta dei post in una singola richiesta?

12 dic 2014, 05:09:07
Visualizzazioni: 44.3K
Voti: 27

Vorrei creare una wp_query che restituisca i meta dei post all'interno dell'array posts.

$args = array (
    'post_type' => 'page',
    'meta_key' => 'someMetaKeyName',
);

// La Query
$query = new WP_Query( $args );

Questo restituisce qualcosa del genere:

esempio di output della query

Come si può vedere i post non hanno alcun meta dato, è possibile includere i meta dati nell'array restituito?

PS Non voglio query wp aggiuntive per ragioni di performance.

1
Commenti

La query standard WP_Query non restituisce i metadati dei post. Le uniche opzioni disponibili sono: 1) eseguire get_post_meta su singole chiavi, 2) eseguire get_post_custom per ottenere tutti i campi personalizzati di un post in una sola operazione, oppure 3) creare la propria query utilizzando la classe $wpdb (get_results()) per costruire il proprio oggetto di ritorno. (Documentazione della classe $wpdb: http://codex.wordpress.org/Class_Reference/wpdb)

BODA82 BODA82
12 dic 2014 06:28:40
Tutte le risposte alla domanda 5
5
26

Di default, WP_Query restituisce gli oggetti standard WP_Post per i post interrogati. Credo che con una riscrittura intelligente e l'uso dei filtri forniti in WP_Query sia possibile aggiungere oggetti all'array di oggetti WP_Post restituiti.

Sarà performante? Secondo me, danneggerà maggiormente le prestazioni poiché sarà necessario unire i risultati nella query, dato che i campi personalizzati non sono salvati nella tabella wp_posts, ma nella tabella wp_postmeta.

Recuperare i meta dei post è molto veloce e non richiede un'istanza aggiuntiva di WP_Query. Puoi semplicemente chiamare il campo personalizzato con get_post_meta(). WordPress è stato molto attento quando sono stati introdotti i campi personalizzati. Hanno aggiunto una cache per memorizzarli, quindi che tu stia interrogando 1 o 100 campi personalizzati, stai colpendo il database una sola volta, in modo super veloce. Per un test completo e una spiegazione, vedi questo post che ho recentemente scritto sull'argomento.

Secondo me, la chiamata aggiuntiva al database e il tempo effettivo speso ne valgono la pena ed è più veloce che riscrivere WP_Query in modo da includere i campi personalizzati nell'oggetto post standard restituito da $posts.

12 dic 2014 06:37:59
Commenti

OK, grazie, accetterò questa soluzione, ma per essere sincero è davvero una seccatura chiamare get_post_meta() per ogni singolo post.. Preferirei che ci fosse un modo per memorizzare dati aggiuntivi direttamente nella tabella wp_posts, o in una tabella correlata che non sia un incubo come wp_postsmeta.

YemSalat YemSalat
12 dic 2014 06:57:01

Beh, a dirla tutta, sia che chiami get_post_meta() sia che lo faccia come oggetto post, dovrai chiamarlo per ogni post. È lo stesso con i tag template come the_content(), devi chiamarlo per ogni post.

Pieter Goosen Pieter Goosen
12 dic 2014 07:02:54

Questo significa che se devi mostrare 120 post avrai 120 query extra nella tua pagina?

chifliiiii chifliiiii
14 set 2015 22:25:00

Tutti i postdata vengono salvati in una cache, quindi non avrai query aggiuntive quando richiami i meta dei post

Pieter Goosen Pieter Goosen
14 set 2015 22:27:53

Hai ragione. Mi riferivo alle post_thumbnails, ma ho recentemente scoperto update_post_thumbnail_cache( $the_query ). Grazie comunque per la precisazione

chifliiiii chifliiiii
14 set 2015 22:50:04
1

Recentemente ho avuto un problema simile, dovevo ottenere 7 metadati da un custom post type, ma anche filtrare i post in base a un pezzo di metadato.

Quindi ho creato la seguente query SQL, che uso spesso. Spero possa aiutare qualcun altro. Proverò a spiegarla al meglio delle mie capacità.

        global $wpdb;
        $pt = 'clients';
        $mk = 'trainerid';
        $mv = $pid;
        $mk1 = 'email';
        $mk2 = 'phone';
        $mk3 = 'gender';
        $mk4 = 'dob';
        $mk5 = 'photo';
        $mk6 = 'registrationts';
        $mk7 = 'activationts';
        $ord = 'p.post_name ASC';

        $sql = "
        SELECT p.ID, p.post_title AS fullname, pm1.meta_value AS email, pm2.meta_value AS phone, pm3.meta_value AS gender, pm4.meta_value AS dob, pm5.meta_value AS photo, pm6.meta_value AS regts, pm7.meta_value AS actemailts
        FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID
            AND pm.meta_key = '{$mk}'
            LEFT JOIN {$wpdb->postmeta} pm1 ON pm1.post_id = p.ID
            AND pm1.meta_key = '{$mk1}'
            LEFT JOIN {$wpdb->postmeta} pm2 ON pm2.post_id = p.ID
            AND pm2.meta_key = '{$mk2}'
            LEFT JOIN {$wpdb->postmeta} pm3 ON pm3.post_id = p.ID
            AND pm3.meta_key = '{$mk3}'
            LEFT JOIN {$wpdb->postmeta} pm4 ON pm4.post_id = p.ID
            AND pm4.meta_key = '{$mk4}'
            LEFT JOIN {$wpdb->postmeta} pm5 ON pm5.post_id = p.ID
            AND pm5.meta_key = '{$mk5}'
            LEFT JOIN {$wpdb->postmeta} pm6 ON pm6.post_id = p.ID
            AND pm6.meta_key = '{$mk6}'
            LEFT JOIN {$wpdb->postmeta} pm7 ON pm7.post_id = p.ID
            AND pm7.meta_key = '{$mk7}'
            WHERE pm.meta_value = '{$mv}'
            AND p.post_type = '{$pt}'
            AND p.post_status NOT IN ('draft','auto-draft')
            ORDER BY {$ord}
        ";

        $clients = $wpdb->get_results( $wpdb->prepare( $sql ), OBJECT );

Per prima cosa ottengo le funzioni del database di WordPress con global $wpdb. Poi imposto il tipo di post con $pt. Per ottenere il post corretto che corrisponde a un valore specifico in post_meta, imposto $mk (meta_key)

Poi imposto la variabile $mv (meta_value) (in questo caso il meta value corrisponde a un postid)

$mk1-$mk7 sono i meta_key che voglio ottenere da ogni post. (Recupererò i valori nella select)

Rendo anche l'ordinamento una variabile, impostando $ord

La select funziona così: Seleziono l'ID del post e il post_title dalla tabella POSTS o 'p.'

Poi seleziono tutti i metadati di cui ho bisogno usando pm1. -> pm7 e prendendo meta_value rinominandoli (AS) per renderli più leggibili quando recupero i dati dal mio oggetto.

Creo un LEFT JOIN per i metadati che devono corrispondere al post. (pm)

Creo 7 left join per ognuno dei metadati che devo recuperare. (pm1-pm7)

La clausola WHERE si basa sul primo LEFT JOIN (pm) così che sappia che voglio solo i post dove i metadati corrispondono.

Aggiungo anche un 'AND' per il tipo di post, e per gli stati del post che non sono bozze. (quindi solo post pubblicati)

Infine aggiungo la clausola 'order by'.

Questa query è veloce e sfrutta gli indici built-in di WordPress, quindi sembra efficiente.

Non so se esiste qualcosa di meglio di questo, ma se c'è, mi piacerebbe usarlo.

Spero sia utile.

Marcus

14 feb 2017 03:21:29
Commenti

Grazie, questo articolo è molto utile. Ho creato una vista con tutti i campi meta di cui ho bisogno e ora è molto veloce e facile ottenere qualsiasi dato desideri

Liko Liko
23 nov 2017 14:01:22
1

Questa domanda ha più di 1 anno, ma ho lo stesso problema, e qui c'è una funzione che aggiungerà ogni meta_value e meta_key all'oggetto $wp_query,

invece di interrogare ogni post meta nel ciclo while, questa funzione farà una query extra esempio:

"SELECT meta_key, meta_value, post_id FROM $wpdb->postmeta WHERE post_id IN (1,2,3,4,5...)"

dove (1,2,3,4,5...) sono gli ID dei post attualmente interrogati da $wp_query

if(!function_exists('add_query_meta')) {
  function add_query_meta($wp_query = "") {

      //Ritorna nel caso in cui wp_query sia vuoto o postmeta esista già
      if( (empty($wp_query)) || (!empty($wp_query) && !empty($wp_query->posts) && isset($wp_query->posts[0]->postmeta)) ) { return $wp_query; }

      $sql = $postmeta = '';
      $post_ids = array();
      $post_ids = wp_list_pluck( $wp_query->posts, 'ID' );
      if(!empty($post_ids)) {
        global $wpdb;
        $post_ids = implode(',', $post_ids);
        $sql = "SELECT meta_key, meta_value, post_id FROM $wpdb->postmeta WHERE post_id IN ($post_ids)";
        $postmeta = $wpdb->get_results($sql, OBJECT);
        if(!empty($postmeta)) {
          foreach($wp_query->posts as $pKey => $pVal) {
            $wp_query->posts[$pKey]->postmeta = new StdClass();
            foreach($postmeta as $mKey => $mVal) {
              if($postmeta[$mKey]->post_id == $wp_query->posts[$pKey]->ID) {
                $newmeta[$mKey] = new stdClass();
                $newmeta[$mKey]->meta_key = $postmeta[$mKey]->meta_key;
                $newmeta[$mKey]->meta_value = maybe_unserialize($postmeta[$mKey]->meta_value);
                $wp_query->posts[$pKey]->postmeta = (object) array_merge((array) $wp_query->posts[$pKey]->postmeta, (array) $newmeta);
                unset($newmeta);
              }
            }
          }
        }
        unset($post_ids); unset($sql); unset($postmeta);
      }
      return $wp_query;
  }
}

Un "postmeta" aggiuntivo verrà scritto per ogni $wp_query->posts[$i]

$wp_query->posts[0]->postmeta

Esempio con 'someMetaKeyName' non dimenticare di inserire

add_query_meta() nel tuo file function.php del tema

$args = array (
    'post_type' => 'page',
    'meta_key' => 'someMetaKeyName',
);

// La Query
$query = new WP_Query( $args );
if($wp_query->have_posts()) {
  $wp_query = add_query_meta($wp_query);
    $i = 0;
    while($wp_query->have_posts()) {
      $wp_query->the_post();
      $post_id = get_the_id();

      //Ottieni $someMetaKeyName nel post corrente
      foreach($wp_query->posts[$i]->postmeta as $k => $v) {
        switch($v->meta_key) {
          case('someMetaKeyName') : {
            $someMetaKeyName = $v->meta_value;
            break;
          }
        }
      }

      //Il tuo codice qui
      //Esempio 
      echo isset($someMetaKeyName) ? '<h3>'.$someMetaKeyName.'</h3>' : '';


      $i++;
    }
}
5 mag 2016 16:10:27
Commenti

Amo questa soluzione.

Armstrongest Armstrongest
1 ago 2017 18:29:58
1

Avevo bisogno di un solo valore extra post_meta (il template nel mio caso) e sono riuscito a ottenere qualcosa di simile a quello che cerchi utilizzando get_pages con un parametro meta_key.

$args = [
  'parent' => $post_id, // nel mio esempio, trovo i figli di un post specifico
  'meta_key' => '_wp_page_template', // cerco il template usato dal post
];

$posts = get_pages($args);

E questo restituisce un array di oggetti post come questo

Array
(
  [0] => WP_Post Object
    (
      [ID] => 22765
      [post_author] => 173
      [post_date] => 2022-03-14 21:11:18
      ...
      [meta_key] => _wp_page_template
      [meta_value] => template-module-content.php // <-- ECCOLO!
      ...
    )

E poi puoi accedere a un meta_value in questo modo

echo $posts[0]->meta_value;

// restituisce 'template-module-content.php' nel mio caso

Ammetto che potrebbe essere un caso d'uso limitato, ma era esattamente quello che mi serviva

16 mar 2022 04:39:25
Commenti

Sembra che WordPress 6.3 abbia ucciso questo piccolo trucco

Todd Todd
10 ago 2023 19:20:17
3
-1

Ehi, per favore prova questo, penso che funzioni bene.

$args = array(
            'post_type' => 'page',
            'meta_key' => 'someMetaKeyName',
            'meta_query' => array(
                array(
                        'key' => 'someMetaKeyName',
                        'type' => 'CHAR',
                   ),
                ),
        );

    $query = new WP_Query( $args );
12 dic 2014 05:44:43
Commenti

Qual è il motivo per cui hai usato sia meta_key che meta_query[]['key']?

kaiser kaiser
12 dic 2014 05:56:06

No, questo non funziona e restituisce l'array di post senza i meta associati.

YemSalat YemSalat
12 dic 2014 05:57:53

meta_key e/o meta_query non modificano il tipo di risultati restituiti, solo la query stessa.

BODA82 BODA82
12 dic 2014 06:39:20