¿Puede wp_query devolver meta datos de entradas en una sola consulta?
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:
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.

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
.

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
.

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.

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

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

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

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

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

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

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

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