¿Puede wp_query devolver meta datos de entradas en una sola consulta?

12 dic 2014, 05:09:07
Vistas: 44.3K
Votos: 27

Me gustaría crear una wp_query que devuelva los meta datos de las entradas dentro del array posts.

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

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

Esto devuelve algo como:

Ejemplo de resultado de WP_Query sin meta datos

Como pueden ver, las entradas no tienen ningún meta dato, ¿es posible incluir los meta datos en el array devuelto también?

PD: No quiero consultas wp_queries adicionales por razones de rendimiento.

1
Comentarios

La consulta estándar WP_Query no devuelve los metadatos de las publicaciones. Las únicas opciones que tienes son: 1) ejecutar get_post_meta en claves individuales, 2) ejecutar get_post_custom para obtener todos los campos personalizados de una publicación de una sola vez, o 3) crear tu propia consulta usando la clase $wpdb (get_results()) para construir tu propio objeto de retorno. (Documentación de la clase $wpdb: http://codex.wordpress.org/Class_Reference/wpdb)

BODA82 BODA82
12 dic 2014 06:28:40
Todas las respuestas a la pregunta 5
5
26

Por defecto, WP_Query devuelve los objetos estándar WP_Post para las publicaciones consultadas. Creo que con una reescritura inteligente y el uso de los filtros proporcionados en WP_Query podrías añadir objetos al array de objetos WP_Post devueltos.

¿Será esto óptimo en rendimiento? En mi opinión, afectará más al rendimiento ya que necesitarás unir resultados en tu consulta, dado que los campos personalizados no se guardan en la tabla wp_posts, sino en la tabla wp_postmeta.

Recuperar metadatos de publicaciones es realmente rápido y no requiere ninguna instancia adicional de WP_Query. Simplemente puedes llamar al campo personalizado con get_post_meta(). WordPress fue muy cuidadoso al introducir los campos personalizados. Añadieron una caché para almacenarlos, así que ya sea que consultes 1 o 100 campos personalizados, solo accederás a la base de datos una vez, superrápido. Para una prueba completa y explicación, revisa esta publicación que hice recientemente sobre este tema.

En mi opinión, la llamada adicional a la base de datos y el tiempo real invertido valen la pena y es más rápido que reescribir WP_Query de tal manera que incluya campos personalizados en el objeto de publicación estándar devuelto por $posts.

12 dic 2014 06:37:59
Comentarios

OK, gracias, aceptaré esta solución, pero para ser honesto, es demasiado engorroso llamar a get_post_meta() para cada publicación individual.. Preferiría que hubiera una forma de almacenar datos adicionales directamente en la tabla wp_posts, o en una tabla relacionada que no sea tan confusa como wp_postsmeta.

YemSalat YemSalat
12 dic 2014 06:57:01

Bueno, para ser honesto, ya sea llamando a get_post_meta() o como objeto de publicación, necesitarás llamarlo para cada publicación. Es lo mismo con etiquetas de plantilla como the_content(), tienes que llamarlo para cada publicación.

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

¿Esto significa que si tienes que mostrar 120 publicaciones tendrás 120 consultas adicionales en tu página?

chifliiiii chifliiiii
14 sept 2015 22:25:00

Todos los datos de la publicación se guardan en una caché, por lo que no tendrás consultas adicionales al llamar a los meta de la publicación

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

Tienes razón. Me refería a las miniaturas de publicaciones pero recientemente encontré update_post_thumbnail_cache( $the_query ). De todos modos, gracias por la aclaración

chifliiiii chifliiiii
14 sept 2015 22:50:04
1

Tuve un problema similar recientemente, necesitaba obtener 7 metadatos de un tipo de entrada personalizada, pero también necesitaba obtener la entrada basada en un metadato.

Así que creé la siguiente sentencia SQL, la uso frecuentemente. Espero que pueda ayudar a alguien más. Intentaré explicarla lo mejor posible.

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

Primero obtengo las funciones de la base de datos de WordPress con global $wpdb. Luego establezco el tipo de entrada con $pt. Para obtener la entrada correcta que coincida con un valor específico en post_meta, establezco $mk (meta_key)

Luego establezco la variable $mv (meta_value). (en este caso el valor meta coincide con un ID de entrada)

$mk1-$mk7 son las meta_keys que quiero de cada entrada. (Obtendré los valores en la sentencia SELECT)

También hago que el 'order by' sea una variable, configurando $ord

La sentencia SELECT funciona así: Selecciono el ID de la entrada y el post_title de la entrada o 'p.'

Luego selecciono todos los metadatos que necesito seleccionándolos con pm1. -> pm.7 y obteniendo el meta_value y renombrándolos (AS) para que sea más legible al recuperar los datos de mi objeto.

Creo un LEFT JOIN para el metadato que necesito que coincida con la entrada. (pm)

Creo 7 LEFT JOIN's para cada uno de los metadatos que necesito recuperar. (pm1-pm7)

La cláusula WHERE se basa en el primer LEFT JOIN (pm) para que sepa que solo necesito las entradas donde coincidan los metadatos.

También agrego un 'AND' para el tipo de entrada, y para los post_status que no sean borradores. (solo entradas publicadas)

Finalmente agrego la cláusula 'order by'.

Esto funciona rápido y con los índices incorporados en WordPress, por lo que parece eficiente.

No sé si hay algo mejor que esto, pero si lo hay, me encantaría usarlo.

Espero que esto ayude.

Marcus

14 feb 2017 03:21:29
Comentarios

Gracias, esta publicación es muy útil. Creé una vista con todos los campos meta que necesito y ahora es muy rápido y fácil obtener cualquier dato que desee

Liko Liko
23 nov 2017 14:01:22
1

Esta pregunta tiene más de 1 año, pero tengo el mismo problema, y aquí está la función que agregará cada meta_value y meta_key al objeto $wp_query,

en lugar de consultar cada post meta en el bucle while, esta función hará una consulta adicional ejemplo:

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

donde (1,2,3,4,5...) son los IDs de posts actualmente consultados desde $wp_query

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

      //Retorna en caso de que wp_query esté vacío o postmeta ya exista
      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;
  }
}

Se agregará "postmeta" adicional a cada $wp_query->posts[$i]

$wp_query->posts[0]->postmeta

Ejemplo con 'someMetaKeyName' no olvides poner

add_query_meta() en tu theme functin.php

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

// The 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();

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

      //Tu código aquí
      //Ejemplo 
      echo isset($someMetaKeyName) ? '<h3>'.$someMetaKeyName.'</h3>' : '';


      $i++;
    }
}
5 may 2016 16:10:27
Comentarios

Me encanta esta solución.

Armstrongest Armstrongest
1 ago 2017 18:29:58
1

Solo necesitaba un valor extra de post_meta (la plantilla en mi caso) y logré obtener algo similar a lo que buscas usando get_pages con un parámetro meta_key.

$args = [
  'parent' => $post_id, // en mi ejemplo, encontrar los hijos de un post específico
  'meta_key' => '_wp_page_template', // buscando la plantilla usada por el post
];

$posts = get_pages($args);

Y esto retorna un array de objetos post como este

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 // <-- ¡BOOM!
      ...
    )

Y luego puedes acceder al meta_value así

echo $posts[0]->meta_value;

// retorna 'template-module-content.php' en mi caso

Admito que podría ser para un caso de uso específico, pero era justo lo que necesitaba

16 mar 2022 04:39:25
Comentarios

Parece que WordPress 6.3 ha eliminado este pequeño truco

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

Hola, por favor prueba este código, creo que funcionará bien.

$args = array(
            'post_type' => 'page', // Tipo de publicación: página
            'meta_key' => 'someMetaKeyName', // Clave meta específica
            'meta_query' => array(
                array(
                        'key' => 'someMetaKeyName', // Clave meta para la consulta
                        'type' => 'CHAR', // Tipo de dato: caracteres
                   ),
                ),
        );

    $query = new WP_Query( $args ); // Ejecutar la consulta
12 dic 2014 05:44:43
Comentarios

¿Cuál es la razón por la que usaste meta_key y meta_query[]['key'] también?

kaiser kaiser
12 dic 2014 05:56:06

No, esto no funciona y devuelve el array de posts sin los meta asociados a ellos.

YemSalat YemSalat
12 dic 2014 05:57:53

meta_key y/o meta_query no modifican el tipo de resultados devueltos, solo la consulta en sí misma.

BODA82 BODA82
12 dic 2014 06:39:20