Hoy en día es bastante habitual en WordPress tener que utilizar Custom Post Types (CPT a partir de ahora) y tener que establecer “relaciones” entre ellos. Un posible ejemplo sería el caso de que en nuestro WordPress tengamos que mostrar información de libros y autores. En este caso, la relación entre libros y autores es m a n, de tal forma que un autor puede tener varios libros y un libro puede ser de varios autores. Vamos, lo habitual en cualquier sistema de base de datos relacional y un ejemplo muy común.

El problema es que WordPress no gestiona estas relaciones como un SGBD relacional sino usando su propia estructura de datos y que no sigue este modelo. Las tablas que WordPress usa para guardar nuestro contenido son wp_posts y wp_posts_meta, estableciéndose una relación 1 a n entre ambas tablas. En estas dos tablas se almacena la información de los posts, paginas, etc. La estructura de campos es la siguiente:

Modelo de datos de wordpress

Esto quiere decir que, aunque creemos varios CPT no se crean tablas adicionales como haríamos en un modelo relacional, sino que toda esta información se almacena en estas 2 tablas (con algunas excepciones que non entraré a comentar).

Estos CPT se pueden crear usando un plugin o directamente en WordPress. En mi caso particular suelo usar Toolset, es un plugin muy completo y que uso habitualmente en la parte de backend para crear los CPT y establecer las relaciones entre ellos. Tiene una parte para frontend que conozco y que he usado en alguna ocasión pero que no sirve para lo que queremos en este caso. Más adelante explicare porque no sirve.

En este ejemplo he creado tres CPT: autores, libros y autores-libros con Toolset . Si quieres mas información de como crear estos CPT te remito a la documentación de Toolset que es muy completa, aquí tienes un enlace que explica como hacerlo.

Como decía no voy a entrar en detalle de como se crean con Toolset, simplemente indicar que los slug de mis CPT son “autor”, “libro” y “autor-libro”.

Vamos con el problema en cuestión. Me hacia falta montar una página que mostrará la portada de los libros con la posibilidad de filtrar por la procedencia del autor y el sexo. Estos dos campos son Custom Fields (CF a partir de aquí) del CPT “autor”. Como se puede ver, los filtros se aplican sobre un CPT y la información que se muestra es de otro CPT, la relación “virtual” entre ambos CPT se establece a través del CPT “autor-libro”, generado también con Toolset.

Inicialmente pensé que usando WP_Query podría realizar una consulta en la que pudiera decirle “selecciona todos los libros de autores de la procedencia=X y de sexo=Y”. Por lo que pude comprobar y probar esto no es posible y tampoco es posible hacerlo usando la parte de Frontend de Toolset. Toolset dispone de una seria de plugins para generar vistas y plantillas a partir de los CPT creados con el propio Toolset pero solo sirven para filtros y listados que usen campos de un CPT, no me permitia filtrar por campos de diferentes CPT como necesitaba en este caso.

Os recuerdo que en este caso quería mostrar información del CPT libros (portada, titulo del libro y enlace al detalle del libro) pero filtrando los datos por campos de otro CPT como es “autor”. En este caso quería filtrar por “sexo” y “procedencia” del autor.

Como comenté anteriormente, usando WP_Query y hasta donde yo sé, no era posible realizar una consulta que uniese los tres CPT y me permitiese filtrar por procedencia y sexo. Es decir, hacer algo similar a lo siguiente:

Como podemos intuir estoy realizando una consulta filtrando por el CF “sexo” (al crearlo con Toolset el slug es wpcf-sexo) y procedencia (al crear este CF el slug es wpcf-procedencia).

Esto no funciona porque WordPress no conoce y no puede gestionar esta relación virtual que yo he establecido con Toolset entre estos CPT y por tanto no puede darme el resultado esperado.

Como esto ni nada parecido funcionaba tenia 2 alternativas o quizá más, por un lado ejecutar directamente una consulta SQL sobre WordPress, usando la clase wpdb   o bien usar WP_Query pero en lugar de hacer una sola consulta realizar varias para obtener el resultado esperado.

La primera opción la intente, pero no fui capaz de crear una consulta SQL que me devolviese los resultados que me hacían falta. Trataré de explicar en el siguiente apartado porque no pude hacerlo

Almacenamiento de los datos en WordPress

Para entender porque no pude hacerlo con una consulta es necesario hacer un inciso para explicar cómo almacena esta información wordpress. Toda la información de los autores y libros se almacena en las tablas wp_posts y wp_postmeta. En este punto, voy a explicar también como almacena Toolset los CF y los CPF para poder establecer la relación “virtual” entre estas tablas.

Cuando se crea un nuevo autor se inserta un registro en la tabla wp_posts similar al siguiente(*)

[table id=1 /]

Esto seria un registro generado al insertar un autor. Asociado a este registro se generan en la tabla wp_postmeta una serie de registros asociados a los CF del autor, por ejemplo para los campos por los que queremos hacer la consulta procedencia y sexo. Como decíamos anteriormente Toolset almacena internamente estos campos con el slug wpcf-procedencia y wpcf-sexo. Esta información aparece en la tabla wp_postmeta para un autor determinado co id 135.

[table id=2 /]

En este caso vemos que se vinculan con el autor por el post_id = 135, que es el mismo ID de la tabla wp_posts.

Además de todo esto, necesitamos saber cómo establecer la relación con los libros que sean de ese autor. También hace falta generar en la tabla wp_posts un registro por cada libro y x registros en la tabla wp_postsmeta con los CF de los libros. Lo vemos en las siguientes tablas para un libro determinado:

[table id=3 /]

Y en el caso de los campos adicionales (CF de libros) en wp_postmeta, como por ejemplo los campos edición y tipo de obra

[table id=4 /]

En todo este proceso queda un último punto para unir todo, que es como se vinculan los libros a los autores. Esto se hace a través del CPT “autor-libro”. Este CPT se define como hijo de libro y de autores cuando se definen en Toolset.  Mas información de esto en el siguiente enlace .

En este caso, Toolset para gestionar estas relaciones, genera un slug de la forma “_wpcf_belongs_xxxx_id”, donde xxxx es el nombre del CPT relacionado, en nuestros casos van a ser “autor” y “libro”. Por tanto, tendrá la forma “_wpcf_belongs_autor_id” y “_wpcf_belongs_libro_id

Y tendremos una entrada en la tabla wp_posts similar a la siguiente:

[table id=5 /]

Y en la tabla wp_postmeta dos entradas que relacionan este registro con el libro y autor correspondiente y que se obtienen a partir del slug anteriores (“_wpcf_belongs_autor_id” y “_wpcf_belongs_libro_id” ) y de los ids de autor(135), libro(511) y autor-libro(512)

[table id=6 /]

Con esto se cierra el circulo y se establece la relación entre autores y libros. Como se puede comprobar toda la información se genera en 2 tablas entre las que no existen relaciones mas allá de las creadas de forma virtual a través de los slugs de los campos y de los CF. La idea de hacer una consulta SQL que me permitiera obtener todos los murales para los autores que sean de un sexo determinado la veía sumamente complicada, así que opte por la segunda opción.

Realizar el filtro

El proceso que iba a implementar se resumía en hacer una primera consulta en la que seleccionasen los libros y otra consulta posterior donde obtuviese los autores de ese libro y comprobase si son del sexo y procedencia que se selecciona en el filtro. Este acercamiento es poco eficiente comparado con una consulta SQL pero no encontré otra alternativa

Toolset acaba de sacar una API que esta en Beta para Toolset Type 3.0 que parece que soporta este tipo de consultas a través de WP_Query . No lo he probado así que tampoco puedo dar más información.

Lo primero que necesitaba era obtener todos los libros usando WP_Query. La consulta pintaba similar a la siguiente:

Una vez hecha esta consulta tenia que obtener los autores de eses libros para ver si cumplían las condiciones del filtro que se estaba aplicando. En este caso, si eran del sexo y procedencia seleccionada. Esto lo hacia dentro del bucle pero primero tenía que consultar la información en “autor-libro” para saber con que autores estaban vinculados los libros obtenidos en la primera consulta.

Vamos a explicar este código con más detalle. Inicialmente obtengo todos los libros con la consulta inicial. En el bucle proceso cada uno de estos libros y para cada uno de ellos obtengo los ids de la relación “autor-libro” para ese libro, que evidentemente pueden ser varios. Es importante indicar que lo que obtengo aquí es el id de la relación “autor-libro”, aún no he llegado a obtener los datos del autor.

Como se puede ver en la creación de la consulta indico que solo me devuelva los ids con                     ‘fields’ => ‘ids’

Una vez que tengo estos ids hago la siguiente llamada

que obtiene los ids en la tabla wp_posts que establecen la relación entre autores y libros del CPT “autor-libro

Después de esto entro en un bucle en el que proceso cada uno de los registros devueltos en esa consulta para saber los ids de los autores y poder ver si cumplen los filtros. Esto lo hago en la siguiente instrucción

Esto me va a devolver un array que contiene todos los registros que existan para ese id en la tabla wp_postmeta, que pueden ser varios, y en el caso que nos ocupa existirán por lo menos estos 2

[table id=7 /]

Fijaros que en la consulta anterior yo voy a estar recuperando estos 2 registros porque realmente estoy consultando por el post_id = 512, que es el id autor-libro.  Una vez obtenidos estos resultados en el array $datos_autor_libro, lo que necesito saber es el id del autor y esto lo obtengo de la siguiente forma


Que me va a devolver el valor 135. Este es el id del autor. Con este id puedo obtener todos los datos de ese autor con

A partir de aquí lo que tengo que hacer, y que no he puesto en el código es comprobar si cumple los filtros que se estén aplicando de sexo y procedencia. Esta comprobación sería algo así

Hasta aquí esta larga explicación. El proceso es un poco farragoso pero no he encontrado otra forma de hacerlo más sencilla. Mi conclusión es que WordPress cuando se quieren tener relaciones 1 a N o M a N entre CPT y mostrar resultados que impliquen filtros entre diferentes “tablas”  y diferentes CF lo hace un poco complicado. No es muy recomendable hacer este tipo de filtros y consultas, aunque muchas veces son necesarios. Una opción sería utilizar categorías y etiquetas pero tampoco me parece una buena solución. Otra cuestión sería poder hacerlo a través de algún plugin que facilitase esta tarea, como parece que lo hace la nueva versión de Toolset pero que aún está en beta.