Twisted by Beyond Neon

En una entrada anterior ya estudiamos cómo funciona el Loop de WordPress y te expliqué con detalle los conceptos básicos para entender la porción de código fundamental que utilizan los temas de WordPress para visualizar los contenidos de tu base de datos.

Hoy iremos un paso más allá y veremos cuatro formas diferentes de personalizar el Loop de WordPress para que se comporte de la manera en que tú quieras que lo haga. Para ello te enseñaré a usar el objeto WP_Query para crear un Loop personalizado. Además, vas a conocer las funciones query_posts() y get_posts(), que también te permitirán manipular el Loop. Y por último, aprovecharás el hook pre_get_posts para modificar la consulta a la base de datos del Loop de WordPress antes de que esta se ejecute, y así cambiar el contenido que se obtiene como resultado.

Todo esto parece muy complicado (y en parte lo es), pero si sigues leyendo atentamente estoy seguro de que comprenderás los conceptos y obtendrás la experiencia suficiente como para subir de nivel en la escala de desarrolladores novatos de WordPress. ¡Atento que esto empieza ya!

Usando el objeto WP_Query

Como ya deberías saber, cuando WordPress recibe una URL del servidor web para procesar, la descompone en una serie de parámetros que utiliza cuando hace la consulta a la base de datos. Si entras en la URL de una entradaWordPress será capaz de obtener el identificador de la entrada a partir de la URL y utilizará este identificador para hacer la consulta en la base de datos. Del mismo modo, si entras en la URL de una categoría, WordPress extraerá el nombre de la categoría de la URL para obtener de la base de datos todas las entradas que pertenezcan a tal categoría.

La consulta a la base de datos se realiza mediante el objeto WP_Query. Concretamente, existe la variable global $wp_query que es usada para recuperar el contenido del Loop principal de una página. Si queremos crear un nuevo Loop personalizado, tendremos que usar una nueva instancia de WP_Query. Ten en cuenta que los Loops personalizados pueden usarse en cualquier archivo de tu tema para mostrar diferentes tipos de contenidos.

Cuando creas un nuevo objeto WP_Query, este contiene funciones por defecto para construir consultas y ejecutarlas para obtener entradas. Nos aprovecharemos de esto para construir nuestras propias consultas personalizadas con los parámetros que queramos para modificar el Loop y así extraer el contenido que queramos de la base de datos.

A continuación, tienes un Loop personalizado usando el objeto WP_Query para obtener y renderizar las entradas cuyo autor sea avillegasn.

Fíjate que en vez de usar los métodos have_posts() y the_post() directamente, tal y como vimos en la explicación del funcionamiento del Loop de WordPress, en este Loop personalizado se llaman los métodos a través de la nueva instancia del objeto WP_Query que hemos creado. Y es en la creación de la nueva instancia (línea 2) cuando especificamos las condiciones para la consulta que incluye nuestro Loop. En este caso, la única condición es author_name=avillegasn, para indicar que sólo devuelva entradas de este autor. Ten en cuenta que si quisieras poner más de una condición deberías separarlas con el símbolo &.

Parámetros para modificar el Loop

Seguramente te preguntarás qué parámetros puedes usar para crear condiciones nuevas cuando creas un Loop personalizado como el que hemos visto antes. La verdad es que hay una infinidad de posibilidades aquí. Las tienes todas en el Codex de WordPress (cómo siempre) bien documentadas y con ejemplos.

Aún así, creo que es oportuno que veas algunos de los parámetros más usados. ¡A por ello!

Usuarios, Categorías y Etiquetas

Puedes decidir devolver sólo aquellos contenidos que pertenezcan a un usuario, categoría o etiqueta concretos usando los siguientes parámetros:

  • cat=3,4,5 – Obtiene las entradas de las categorías indicadas mediante el identificador (o identificadores, si hay mas de uno, separados por comas).
  • category_name=comunidad – Obtiene las entradas de la categoría indicada mediante el nombre de categoría.
  • tag=wprofesional – Obtiene las entradas de la etiqueta indicada mediante el nombre de  etiqueta.
  • tag_id=34 – Obtiene las entradas de las etiquetas indicadas mediante el identificador (o identificadores, si hay mas de uno, separados por comas).
  • author=1 – Obtiene las entradas que tengan como autor a los usuarios indicados mediante el identificador (o identificadores, si hay mas de uno, separados por comas).
  • author_name=avillegasn – Obtiene las entradas que tengan como autor al usuario cuyo Nombre de usuario sea el indicado.

Entradas y Páginas

Del mismo modo, también podemos hacer uso de parámetros específicos para seleccionar sólo ciertas entradas y páginas concretas:

  • p=135 – Obtiene la entrada que tenga el identificador indicado.
  • name=mi-entrada – Obtiene la entrada cuyo slug o enlace permanente sea el indicado.
  • page_id=8 – Obtiene la página que tenga el identificador indicado.
  • pagename=mi-pagina – Obtiene la página cuyo slug o enlace permanente sea el indicado.
  • post_type=page – Obtiene sólo páginas.
  • post_type=post – Obtiene sólo entradas.

Ejemplos más complejos

Como podréis ver en el Codex, se pueden hacer consultas mucho más complicadas. En el siguiente ejemplo, seleccionamos las entradas personalizadas del tipo amigo ordenadas por el campo personalizado edad, quedándonos con aquellas cuya edad esté entre 18 y 35.

Usando query_posts()

A pesar de que el método más habitual para modificar el Loop de WordPress es mediante el uso del objeto WP_Query, existen otros mecanismos que también puedes usar.

La función query_posts() se utiliza para modificar los contenidos devueltos por el Loop principal de WordPress. Concretamente, puedes modificar el contenido devuelto en $wp_query después de que la consulta por defecto se haya ejecutado, cambiar los parámetros de consulta, y repetir la consulta usando query_posts(). El mayor problema de usar query_posts() es que sobrescribe el resultado original del Loop principal, con lo cual hay que ser muy cuidadoso con ella.

La función query_posts() la usarás justo antes del inicio del Loop, tal y como vemos a continuación:

En este ejemplo hemos cambiado el Loop indicando a WordPress que sólo muestre aquellas entradas que pertenecen a la categoría que tiene como identificador el número 1 y que además están etiquetadas con las etiquetas temas y plugins.

Como ya hemos dicho, usar la función query_posts() tal cual sobreescribe el contenido extraído por el Loop. Esto significa que los contenidos anteriores ya no se devolverán, lo cual es lógico puesto que estamos cambiando la consulta a la base de datos desde query_posts(). Para evitar perder el contenido del Loop original (el que venía antes de que pusieras la función query_posts()) puedes guardar los parámetros de consulta usando la variable global $query_string:

En este ejemplo concatenamos los parámetros adicionales a los parámetros de consulta que ya están incluidos en $query_string. De esta forma lo que estamos haciendo es filtrar la consulta inicial haciéndola más estricta de lo que esta era (indicando que sólo devuelva las entradas del autor avillegasn).

Usando get_posts()

Una alternativa más sencilla al uso de query_posts() es la función get_posts(). Esta simplemente retorna información de entradas. El principal problema es que no tiene en cuenta la configuración de los metadatos necesarios para el uso de Template Tags. Para arreglar esto, hay que usar la función setup_postdata(). En el siguiente ejemplo vemos cómo se haría esto:

Otra diferencia es que los contenidos que devuelve get_posts() están en forma de array. Por eso usamos la sentencia foreach para recorrerlo (nos hace de Loop).

Aunque encontrarás código antiguo que usa get_posts() o query_posts(), el método preferido para trabajar con Loops es mediante WP_Query().

Usando el hook pre_get_posts

Por último, disponemos de una opción adicional para modificar cualquier Loop en tu WordPress mediante el hook pre_get_posts. Habitualmente, este es el hook preferido para modificar el Loop principal de WordPress.

El hook pre_get_posts tiene como parámetro la consulta global de WordPress, lo que te permite modificarla antes de que se ejecute y devuelva valores. Por tanto, podemos modificar la consulta a la base de datos de WordPress y hacer que incluya nuevas condiciones que modifiquen el contenido que se devolverá una vez se ejecute.

Para usar el hook pre_get_posts, habitualmente añadiremos código en el archivo functions.php de nuestro tema. Veamos un ejemplo:

Fíjate que estamos usando funciones condicionales (is_admin(), is_main_query(), is_search()) para modificar el Loop únicamente en areas específicas de WordPress. Concretamente, sólo se mostraran entradas (ver $query->set( 'post_type', 'post' ) en la linea 4) en los resultados de una búsqueda en tu web (ver $query->is_search() en la linea 3). Por tanto, hemos modificado el Loop para que sólo muestre entradas (y no páginas, ni productos, ni ninguna otra cosa más) únicamente en los resultados de búsquedas. En cualquier otro lugar, el Loop principal se comportará tal y como ya lo hacía, dado que las funciones condicionales evitarán la ejecución de la función set() de la línea 4.

El hook pre_get_posts filtra el objeto WP_Query, por lo que cualquier cosa que puedas hacer con WP_Query (como vimos anteriormente) lo puedes hacer en pre_get_posts usando la función set(). Esto incluye el uso de los parámetros que te expliqué antes para modificar el Loop.

Para más información sobre el hook pre_get_posts, visita la página del Codex http://codex.wordpress.org/Plugin_API/Action_Reference/pre_get_posts.

Repaso final

Recuerda que el Loop es el corazón de los temas WordPress. Cualquier tema que se precie usa Loops para recuperar el contenido y mostrarlo con los estilos oportunos. Pero no sólo los temas usan Loops, también los plugins. Mostrar entradas relacionadas o desarrollar un widget para la barra lateral que muestre las entradas más visitadas son funciones que un plugin puede añadir haciendo uso de Loops personalizados.

Hemos visto cuatro maneras diferentes de modificar el Loop principal de WordPress o crear Loops personalizados. Has aprendido a usar el objeto WP_Query para crear un Loop nuevo y filtrar los resultados a partir de una serie de parámetros. Además, has conocido las funciones alternativas query_posts() y get_posts() y los inconvenientes que tienen. Por último, también has visto el hook pre_get_posts que permite modificar todos los Loops de WordPress desde un único punto en el código. Tendrás que ir con cuidado al usarlo, ya que es un arma tan potente que en manos inexpertas puede causar el caos.

Si has llegado hasta aquí, te felicito. Has superado una nueva etapa en tu formación como desarrollador WordPress. Y recuerda que no hemos acabado. En una futura entrada veremos ejemplos concretos sobre cómo crear y trabajar con múltiples Loops a la vez. Y si tienes alguna duda, no dudes en comentarla para que podamos mejorar.

Imagen destacada de Beyond Neon

23 respuestas a «Cómo modificar el Loop de WordPress»

  1. Avatar de Gastón Castagnet

    Hola Antonio, mucho gusto, tengo una consulta, espero puedas ayudarme.
    Tengo una página de productos, la cual al cargar por primera vez, lista todos los productos, y completa un SELECT con las custom_field «Categoria», las mismas las utilizaré para filtrar productos. Hasta aquí todo ok.

    Cuando un usuario selecciona una categoría y ejecuta el submit… mediante pre_get_post() tomo ese filtro y muestro correctamente los productos de esa categoría. Esto también funciona ok! … El problema que tengo es que el SELECT con las «Categorías» no se vuelve a cargar.

    Después de leer tu artículo, pienso que al utilizar pre_get_post(), y modificar el query global, ya no puedo consultar por mi custom_fild categorías. Esto es así? Cómo puedo solucionarlo?… espero haberme explicado correctamente.

    Muchas gracias. Saludos

    1. Avatar de Antonio Villegas

      No tengo muy claro cual es el problema, sin embargo ten en cuenta que pre_get_posts modifica todas las queries. Deberías poner condicionales para que sólo modifique el caso que necesitas. Otro posible problema es que no resetees bien alguna de las queries anidadas. En la segunda parte del tutorial tienes información al respecto. Espero que te sirva algo de lo que te comento. Un saludo, Gastón.

  2. Avatar de David
    David

    Muchas gracias Antonio por este tutorial para ayudar a los que estamos empezando a subir de nivel.
    He probado el primer ejemplo cambiando tu usuario por el mio y me da error php en la línea del endif. Comparando con el último código de tu entrada anterior (Cómo funciona el Loop de WordPress) veo que aquí se invierte el orden entre endif y endwhile. Pero si le cambio el orden no da error pero no imprime nada. No entiendo donde está el error.

    A ver si me puedes arrojar un poco de luz. Muchas gracias

    1. Avatar de Antonio Villegas

      Hola David. Tienes razón, había un error. El endif y el endwhile estaban invertidos. Ahora ya los he arreglado.

      Que no se imprima nada es normal. Para imprimir algo tienes que usar alguna función en el lugar donde está el comentario. Por ejemplo, puedes probar a poner algo así como echo the_title(); para que se pinte el título de la entrada. Espero que esto te sirva y gracias por avisarnos del fallo 😉

  3. Avatar de Sergi
    Sergi

    ¡Muy interesante!

    Se agradece también la buena explicación.

    En mi caso he intentado modificar el loop del index.php para que wordpress muestre en el home aquellas entradas que pertenecen a una categoria en concreto. Funciona bien, el problema es que me anula la paginación. Crea las páginas pero muestra las mismas entradas en cada una de las páginas. Sucede usando tanto el objeto WP_query como query_posts ();

    Saludos

    1. Avatar de Antonio Villegas

      Hola Sergi. Sin ver lo que has hecho es complicado saber cual es el problema, pero debería funcionar si sólo has modificado la query que se hace a WordPress. Repasa el código original y los cambios que has hecho, ya que algo debe estar incorrecto.

  4. Avatar de Gerardo Velez
    Gerardo Velez

    Buenas Noches, excelente web. De verdad sirve de ayuda

    Yo tengo un problema puntual. Tengo un nuevo post type que le puse de nombre sistema_de_novedades. Dentro, usando un plugin tengo una serie de campos personalizados donde recojo la informacion. Uno de ellos se llama tipo_de_novedad

    El tema es que realizo la consulta usando wp_query y me retorna todos los posts que tengan el post_type=sistema_de_novedades

    Pero no veo manera de que me devuelva solo los que cumplan la condicion tipo_de_novedad=Material POP

    Estoy tratando de algunas maneras, cambiando las consultas, poniendo en arrays, intente ahora con las funciones que vi en este tutorial, pero el resultado es el mismo. Me lista todas las novedades sin importar cual sea su tipo.

    Gracias de antemano por tu tiempo para leer y responder las dudas de tus lectores

    Un abrazo

    Gerardo

    1. Avatar de David Aguilera

      Hola Gerardo,

      Echa un vistazo a la opción meta_query de las WP_Query:

      $args = array(
      ‘post_type’ => ‘sistema_de_novedades’,
      ‘meta_query’ => array(
      array(
      ‘key’ => ‘tipo_de_novedad’,
      ‘value’ => ‘Material POP’,
      ‘compare’ => ‘=’,
      ),
      ),
      );
      $query = new WP_Query( $args );

  5. Avatar de Juan Diego
    Juan Diego

    Hola gracias por los aportes!!!
    Pregunta:
    Si uso varios Loop personalizados.
    Hay forma de evitar se que repitan las entradas entre diferentes Loop? Suponiendo que entre ellos se comparte algún parámetro de selección. Saludos

    1. Avatar de Antonio Villegas

      Hola Juan. Para evitar repeticiones has de controlar tú que no pase. Tienes el parámetro post__not_in como argumento para cuando haces consultas.

  6. Avatar de Sebastián Villar
    Sebastián Villar

    Hola Antonio. Muchas gracias por este post. Aprovecho para consultarte sobre un problema que no logro resolver. Tal vez puedes ayudarme.

    Customizamos el loop del archive del post type ‘movie’ con el siguiente $args:

    $args = array(
    ‘post_type’ => ‘movie’,
    ‘post_status’ => ‘publish’,
    ‘posts_per_page’ => 10,
    ‘meta_type’ => ‘NUMERIC’,
    ‘meta_key’ => ‘themeum_anio_final’,
    ‘order_by’ => ‘meta_value_num’,
    ‘order’ => ‘DESC’,
    ‘paged’ => max( 1, get_query_var(‘paged’) )
    );

    $posts = new WP_Query($args);

    Lo necesitamos ordenado por el meta_value de la meta_key ‘themeum_anio_final’.

    El problema es que en los meta_value de la meta_key ‘themeum_anio_final’ tenemos por ejemplo valores que podrían convertirse a númericos como ‘1956’, ‘2018’ pero también string como ‘sin dato de fecha’.

    Entonces creo que al tener el valor ‘sin dato de fecha’ no logra convertir los meta value a números para ordenarlos…

    Ya probé utilizando el ‘order by’ => ‘meta_value meta_value_num’ y tampoco.

    También probé utilizar pre_get_posts() y me genera un error 404.

    Muchas gracias desde ya!

    1. Avatar de Antonio Villegas

      Habría que mirarlo con más detalle en tu instalación, pero así a bote pronto se me ocurre que los valores ‘sin dato de fecha’ los limpiéis para poner nulls o bien eliminar el postmeta para esos casos. De este modo tendréis los valores normalizados a sólo números. Podéis probar a ver qué tal…

  7. Avatar de Virginia
    Virginia

    Hola, gracias por el blog, estoy apenas comenzando a utilizar wordpress y necesito hacer una modificación, ordenar los resultados por precio de mayor a menor y viceversa, pero no he conseguido hacerlo, intenté hacerlo a través de $arg = add_query_arg( ‘_price’,’DESC’, $_SERVER[‘REQUEST_URI’]); y nada; es como si no existiera. Me sigue mostrando por orden de registro de entradas.
    Agradezco cualquier guía.

    1. Avatar de Antonio Villegas

      Hola Virginia. Depende de cómo tengas guardado el valor de precio. Te recomiendo que te leas la documentación de WP_Query, y más concretamente el apartado de ordenación aquí.

  8. Avatar de Ing. Daniel
    Ing. Daniel

    Buen articulo me ayudo mucho, pero tengo una cuestión, quiero usar el loop para mostrar entradas en mi index pero por diseño solo quiero mostrar 3, entonces como hago para que el loop solo realice 3 recorridos.

    Lo se al hacer eso dejaria de ser loop, o que puedo usar para llamar las entradas.

    1. Avatar de Antonio Villegas

      Hola Daniel. Tienes que poner posts_per_page=3 en los argumentos de WP_Query.

  9. Avatar de Abiel M
    Abiel M

    Hola! muchas gracias por la informacion me sirvio muchisimo! Podrias aclararme en breve las siguientes preguntas:

    1. Cuando conviene usar el get_posts( )?

    2. get_posts( ) es otra forma de acceder al query principal o es un query secundario?

    3. Si solo necesito el post mas reciente, es decir, solo 1 post, conviene usar aqui el get_posts( ) o conviene hacer una instancia de WP_Query?

    Gracias de antemano!

    1. Avatar de Antonio Villegas

      Hola Abiel,

      1. Usa get_posts() cuando quieras recuperar posts.
      2. Es una consulta independiente de la consulta principal (suponiendo que estás dentro de un loop).
      3. Puedes hacer un get_posts() directamente.

      Espero haberte ayudado.

  10. Avatar de Ariel Castillo
    Ariel Castillo

    Actualmente estoy usando este código

    el cual me muestra las últimas tres entradas. Mi pregunta es: ¿Cómo puedo hacer que me muestre sólo la penúltima entrada, por ejemplo?
    Mucha sgracias.

    1. Avatar de Antonio Villegas

      Así a bote pronto se me ocurre que pidas sólo una entrada (argumento posts_per_page=1) y pongas un offset. El código sería tal que así:

      $query = new WP_Query( array( 'posts_per_page' => 1, 'offset' => 1 ) );

  11. Avatar de Juan Aurelio Pecino
    Juan Aurelio Pecino

    buenos días, quisiera hacer algo en mi wordpress pero no encuentro información de como hacerlo.
    Mostrar en un página o un witgets un listado de post publicados hace x años, hace 10 años por ejemplo…

    1. Avatar de Antonio Villegas

      Busca algún plugin que haga esto o bien crea el listado a mano. Depende de si la información ha de ser dinámica o no.

    2. Avatar de David Aguilera

      ¡Hola! WordPress incluye un widget para mostrar entradas recientes por defecto. Si echas un vistazo a la documentación, verás que dicho widget dispone de un filtro (widget_posts_args) que permite modificar la query que se lanza para recuperar las entradas que se mostrarán en el widget. Entonces, lo único que debes hacer es crear una función propia que se enganche a ese filtro y añada la restricción de que la entrada tenga una cierta fecha. Algo en plan $args['date_query'] = array( 'before' => '2010' );.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

He leído y acepto la política de privacidad de Nelio Software

Tus datos personales se almacenarán en SiteGround y serán usados por Nelio Software con el único objetivo de publicar tu comentario aquí. Con el envío de este comentario, nos das el consentimiento expreso para ello. Escríbenos para acceder, rectificar, limitar o eliminar tus datos personales.