Consultas complejas a wp_postmeta
Consultas complejas a wp_postmeta
Una de las grandes ventajas de usar WordPress es que gracias a su sistema de campos meta podemos crear cualquier contenido sin necesidad de modificar la estructura de tablas de WordPress. No necesitamos nuevas tablas para almacenar tipos complejos. Si queremos guardar un campo más, nos basta con guardarlo como campo meta asociado a la ID de la entrada (entrada, página, o tipo personalizado), y es muy fácil de gestionar gracias a update_post_meta y get_post_meta.
Pero bien es cierto que esta funcionalidad carece de algo básico. No podemos consultar de forma sencilla qué entradas se corresponden con dos o más valores para varias claves meta. Voy avisando desde ya que nos va a tocar crear sentencias SQL para poder hacer consultas complejas a wp_postmeta.
Recientemente me he topado con este problema, y estuve varias horas pensando en la forma más sencilla de solucionarlo. En realidad, como poder, podemos hacerlo a lo «Quién es Quién», es decir, preguntando clave a clave qué IDs corresponden e ir cerrando el círculo de posibles resultados. Pero es engorroso, ¿No podríamos obtenerlo todo de una vez? Bueno, todo esto lo vamos a ver mejor con un ejemplo…
Supongamos que tenemos la siguiente información almacenada para nuestro tipo personalizado «Coche». ¿Por qué lo hago con un tipo personalizado? Porque con ellos siempre caemos en un error básico, que luego resaltaré, pero esto también es válido para entradas y páginas. Lo dicho, supongamos que para nuestro tipo «Coche», tenemos tres campos en wp_postmeta: «Color», «Fabricante» y «Modelo».
La forma lenta
Explicaré primero la forma «lenta y tediosa» de hacerlo. Queremos buscar aquellos coches cuyo color sea «Rojo», su fabricante «Renault» y el modelo «Megane». Ya de entrada, aunque solo quisieramos buscar el color, esto requiere una sentencia SQL. WordPress no ofrece una forma fácil de hacerlo (aunque en realidad no debe ser complicada, y desde aquí animo al grupo de desarrollo de WordPress a hacer una función que devuelva el post_id según claves y valores de la tabla wp_postmeta).
a) Obtenemos los post_id de la tabla wp_postmeta donde meta_key sea «color» y meta_value sea «Rojo».
<?php global $wpdb; $results = $wpdb->get_results("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = "color" AND meta_value = 'Rojo'"); $results = implode(",",$results);
b) Ahora, repetimos, pero añadimos una condición más, la de que post_id debe estar dentro de $results, además de cambiar a buscar por fabricante.
$results = $wpdb->get_results("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = "fabricante" AND meta_value = 'Renault' AND post_id IN ($results)"); $results = implode(",",$results);
c) Ya os podéis imaginar la última línea…
$results = $wpdb->get_results("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = "modelo" AND meta_value = 'Megane' AND post_id IN ($results)"); $results = implode(",",$results);
Y porque ahora tenemos tres campos… Cuando tengamos más, puede resultar un poco tedioso. Finalmente, $results contiene un array con los IDs de aquellos coches que cumplen las tres condiciones.
La forma elegante y eficiente
En realidad, esta forma no es mucho menos tediosa, puesto que con muchos campos a buscar la sentencia va a quedar bastante larga, pero queda mucho más elegante (y sólo tienes que hacer una llamada a la base de datos, con lo cual, también es más eficiente). Consiste en una sola consulta, a la que añadiremos, mediante INNER JOIN, la misma tabla tantas veces como la necesitemos.
$query = "SELECT key1.post_id FROM $wpdb->postmeta key1 INNER JOIN $wpdb->postmeta key2 ON key1.post_id = key2.post_id AND key2.meta_key = 'fabricante' AND key2.meta_value = 'Renault' INNER JOIN $wpdb->postmeta key3 ON key1.post_id = key3.post_id AND key3.meta_key = 'modelo' AND key3.meta_value = 'Megane' WHERE key1.meta_key = 'color' AND key1.meta_value = 'Rojo' "; $results = $wpdb->get_col($wpdb->prepare($query));
Como véis, unimos tres veces la tabla postmeta, como factor común post_id, y a cada una de las tres tablas le ponemos dos condiciones (meta_key y meta_value). Esto nos devuelve todas las IDs de los coches que cumplen las tres condiciones.
Obtención del array de Coches
Ahora sólo hay que hacer el get_posts apropiado para obtener los datos del tipo personalizado:
$coches = get_posts ( array ( 'post__in'=>$results ) );
¡Precaución, amigo conductor! La senda es peligrosa… Esta línea de código, como habrás podido comprobar a estas alturas… no funciona.
$coches = get_posts ( array ( 'post__in'=>$results, 'post_type'=>'coche' ) );
Este es el error básico del que hablaba antes. Cuando llamamos a la función get_posts se nos suele olvidar especificar el tipo de post. Pensamos que sólo con la ID ya va a buscar correctamente, y no es así. El atributo «post_type» tiene valor por defecto: «post», por lo tanto, sólo con nuestras IDs, lo único que conseguimos es que busque esas IDs entre las Entradas (y evidentemente no va a encontrar nunca ninguna, porque, en este caso, esas IDs pertenecen al tipo Coche).
Ahora podéis decidir vosotros qué método utilizar, que todos los caminos llevan a Roma.
«Hazlo todo tan simple como sea posible, pero no más simple»
– Albert Einstein
1 Comentarios
carlos
Hola buenas, veo que estás puesto en consultas de Wordpress y me gustaría que me echases una mano si eres tan amable. Tengo un problema y es que necesito mostrar latitudes y longitudes que tengo guardadas en wp_usermeta haciendo un array o un while... necesitaria mostrar esto.. <marker id="id de usuario'" lat="'valor de meta_value de metakey lat'" lng="'valor de meta_value de metakey lng'" />'; }while($row=mysql_fetch_array($resultado)); echo '</markers>';