WP_Query - ¿Filtrar o directamente?

2 ene 2016, 05:01:18
Vistas: 13.5K
Votos: 5

La cantidad de mis archivos, plantillas, scripts, consultas, etc. está creciendo y necesito un buen sistema para mantener todo organizado.

He intentado mantener todo ordenado:

  • Sin etiquetas <script> en las plantillas
  • CSS inline solo cuando es una variable PHP que puede cambiarse dinámicamente desde las opciones del admin
  • Archivos únicos grandes de JS y CSS para minimizar las peticiones

Ahora es momento de organizar mis consultas porque tengo al menos 10 y siguen aumentando.

  1. Primera opción: usar add_filter() personalizado con cada consulta

    • No tengo que buscar las consultas porque todas están en un archivo o directorio
    • Si necesito cambiarla, solo debo modificarla en un lugar, no en todas mis plantillas
  2. Segunda opción: escribir todas las consultas directamente en las plantillas como se suele hacer

    • Básicamente cada punto es lo contrario de la primera opción

Pregunta:

¿Usar filtros para los argumentos de las consultas tiene alguna desventaja? ¿Rendimiento? ¿Algo más?


Ejemplo:

  1. Usual:

    $args = array(
    
            'post_type'         => 'my-post',
            'posts_per_page'    => 8,
            'orderby'           => 'rand', 
        );
    
    }
    
    $results = new WP_Query( $args );
    
  2. Filtro:

    //En un archivo -> fácil de encontrar y cambiar
    add_filter( 'some_args', 'some_search_args' );
    
    function some_search_args( $search_args ) {
    
        $search_args['post_type'] = 'property';
        $search_args['posts_per_page'] = 8;
        $search_args['orderby'] = 'rand';
    
        //Todo tipo de lógica y código condicional puede ir aquí
    
        return $search_args;
    }
    
    
    //Y
    
    
    //Solo incluir así en cualquier plantilla que quieras y tantas veces como necesites
    //Para cambiar la consulta, solo tendrás que modificar el código de arriba
    $search_args = array();
    $search_args = apply_filters( 'some_args', $search_args );
    
    $results = new WP_Query( $search_args );
    
0
Todas las respuestas a la pregunta 1
4

Debes considerar varias cosas aquí y parece que buscas mejorar el rendimiento de una consulta. La primera y más importante pregunta que debes hacerte es:

¿Necesito una consulta personalizada?

Hice una publicación extensa sobre este tema hace un tiempo que deberías revisar. Si respondiste que sí a la pregunta anterior después de leer mi publicación en el enlace, entonces debes considerar lo siguiente al crear consultas personalizadas:

  • Evita (cuando puedas) operaciones complejas de orderby como ordenar por valores meta. SQL no es el mejor para ordenar y PHP a veces es más rápido. Tiendo a preferir usort() para ordenamientos complejos para ahorrar recursos. El orden aleatorio también es muy costoso en recursos.

  • Evita (cuando puedas) construir consultas complejas con meta y tax queries anidados, especialmente con muchos operadores OR. Estos son bastante exigentes en recursos.

  • Evita (cuando puedas) usar operadores LIKE en tu SQL generado. Estos también son costosos.

  • Usa transients (y cachés) para almacenar consultas costosas. Para consultas aleatorias, no puedes hacer esto, así que necesitarás buscar otros métodos para abordar este problema.

  • Siempre evita el típico bucle foreach donde obtienes una lista de términos y luego ejecutas una consulta personalizada para cada uno, son realmente costosos. En su lugar, consulta todos los posts de una vez y luego usa usort() para ordenar los resultados.

  • Crea una consulta según lo que necesites. La mayoría de las veces solo necesitamos consultar posts para obtener sus IDs y pasarlos a otra función. En casos como estos, solo consulta los IDs de los posts. Esto realmente ahorra muchos recursos. Simplemente agrega 'fields'=>'ids', a los argumentos de tu consulta.

  • Para acelerar consultas no paginadas, usa get_posts() o simplemente pasa 'no_found_rows'=>true a WP_Query (esto es exactamente lo que hace `get_posts). Esto omite el proceso de paginación y ahorra muchos recursos en bases de datos grandes.

Esto solo pretende ser una guía para acelerar la consulta. Todavía hay otras cosas para acelerar las consultas.

¿Usar filtros para los argumentos de consulta tiene alguna desventaja? ¿Rendimiento? ¿Algo más?

No veo por qué podría haber problemas. Si estás creando un tema comercial, definitivamente lo estás haciendo correctamente. Hacer algo filtrable hace la vida de los autores de temas hijos mucho más fácil. Podría costar una milésima de una milésima de una milésima de segundo, pero definitivamente es tiempo bien empleado. Es como la sanitización. La sanitización cuesta tiempo y recursos (aunque es muy, muy poco), pero gastar un milisegundo más en algo puede salvar tu sitio de ser hackeado y destruido.

En mi humilde opinión, necesitarías buscar otras formas de acelerar una consulta sin comprometer la usabilidad y mantenibilidad. La opción 2 es definitivamente algo que deberías hacer para temas comerciales.

IDEA (Podría ser un poco exagerado ;-))

También puedes usar pre_get_posts para filtrar tu consulta personalizada y hacerla filtrable. Es tan simple como establecer tu propio parámetro personalizado en tu consulta y luego usar ese parámetro para dirigir tu consulta.

En el siguiente ejemplo, usaremos un parámetro personalizado query_no al que le daremos valores numéricos.

Las Consultas

$q1 = new WP_Query( ['query_no' => 1] );    
$q2 = new WP_Query( ['query_no' => 2] );    
$q3 = new WP_Query( ['query_no' => 3] );    

pre_get_posts

add_action( 'pre_get_posts', function( $q ) 
{
    if ( $q->get( 'query_no' ) == 1 ) {
        $q->set( 'posts_per_page', -1 );
        // Agrega cualquier otro argumento adicional para establecer
    }

    if ( $q->get( 'query_no' ) == 2 ) {
        $q->set( 'post_type', ['post', 'page'] );
        // Agrega cualquier otro argumento adicional para establecer
    }

    if ( $q->get( 'query_no' ) == 3 ) {
        $q->set( 'post_status', 'trash' );
        // Agrega cualquier otro argumento adicional para establecer
    }
} );

El usuario ahora puede agregar argumentos adicionales o cambiar los pasados.

add_action( 'pre_get_posts', function( $q ) 
{
    if ( $q->get( 'query_no' ) == 2 ) {
        // Añadamos otro tipo de post
        $post_types = $q->get( 'post_type' );
        $post_types = array_merge( $post_types, ['my_post_type'] );

        $q->set( 'post_type'     , $post_types );
        $q->set( 'posts_per_page', -1          );
        // Agrega cualquier otro argumento adicional para establecer
    }

}, 
11 // Asegúrate de que esto se ejecute después de la acción por defecto
); 
2 ene 2016 09:02:11
Comentarios

¿Qué opinas sobre los transients cuando los resultados de las consultas son los mismos? Un ejemplo simple pero ilustrativo: se hace una consulta vía AJAX -> se establece un transient -> y cuando el usuario hace spam en el botón sin cambiar los campos de entrada (he configurado protección contra spam para los botones AJAX pero aún así...) -> obtener los resultados de los transients? Pregunto esto porque mi gran consulta está vinculada con un mapa y el desplazamiento/zoom llama a otra consulta exactamente igual porque mostrar ej. 100,000 marcadores mata cualquier navegador.

N00b N00b
2 ene 2016 12:56:22

Simplemente revisa las publicaciones vinculadas bajo usort(). Yo creo nombres únicos para los transients tomando los argumentos, luego uso md5() para crear una clave única que agrego al nombre del transient. De esta manera, si los argumentos de la consulta cambian, se crea un transient único. Cualquier otra llamada posterior con los mismos argumentos simplemente carga los resultados desde el transient.

Pieter Goosen Pieter Goosen
2 ene 2016 13:46:27

Lo siento, debí estar ciego porque no lo vi antes... Gracias por la respuesta tan detallada.

N00b N00b
2 ene 2016 13:49:47

No hay problema, todos todavía sentimos los efectos posteriores del año nuevo, jajajaja. Disfruta ;-)

Pieter Goosen Pieter Goosen
2 ene 2016 13:50:47