¿Cómo crear una abstracción flexible para WP_Query?

8

Mi pregunta es sobre php pero involucra wordpress ya que estoy creando un complemento. El caso es que tengo 5 preguntas, cada pregunta tiene 6 opciones y una opción para elegir de cada una. Ahora la persona seleccionaría cualquier opción de cada o solo unas pocas. He creado la condición if que ahora me está volviendo loco, ya que ha pasado demasiado tiempo y hará más, como se harán casi 100 combinaciones. No me gustaría eso, sé que hay una forma de matriz multidimensional, pero no soy un plugin o experto en php para WordPress. así que si alguien puede arreglarlo por mí.

$qs = $_POST['q1'];
$q2 = $_POST['q2'];
$q3 = $_POST['q3'];
$q4 = $_POST['q4'];
$q5 = $_POST['q5'];
$q6 = $_POST['q6'];



 $args = array(
  'post_type' => 'product',
  'posts_per_page' => -1,
  'tax_query' => array(
    'relation' => 'AND',
    array(
     'taxonomy' => 'product_cat',
     'field' => 'slug',
     'terms' => 'fashion-follower'
    ),
//    array(
//     'taxonomy' => 'product_cat',
//     'field' => 'slug',
//     'terms' => 'cheap-and-cheerful'
//    )
  )
);
 //The Fashionsia
 if (($qs ==='party') && ($q2 === 'clothes') && ($q3 === 'shopping') && ($q5 === 'Sunbathing') && ($q6 === 'mini')){

$query = new WP_Query( $args );
if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

     the_post_thumbnail('thumbnail');

endwhile;
    endif;      
}

//second question loop

$args2 = array(
  'post_type' => 'product',
  'posts_per_page' => -1,
  'tax_query' => array(
    'relation' => 'AND',
    array(
     'taxonomy' => 'product_cat',
     'field' => 'slug',
     'terms' => 'the-homemaker'
    ),
//    array(
//     'taxonomy' => 'product_cat',
//     'field' => 'slug',
//     'terms' => 'cheap-and-cheerful'
//    )
  )
);
 //The homemaker
 if (($qs ==='drink') && ($q2 === 'candles') && ($q3 === 'house') && ($q4 === 'diy')){

$query = new WP_Query( $args2 );
if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

     the_post_thumbnail('thumbnail');

endwhile;
    endif;      
}
//third loop

$args3 = array(
  'post_type' => 'product',
  'posts_per_page' => -1,
  'tax_query' => array(
    'relation' => 'AND',
    array(
     'taxonomy' => 'product_cat',
     'field' => 'slug',
     'terms' => 'entertainment'
    ),
//    array(
//     'taxonomy' => 'product_cat',
//     'field' => 'slug',
//     'terms' => 'cheap-and-cheerful'
//    )
  )
);
 //The Entertainer
 if (($qs ==='party-babe') && ($q2 === 'winer')&& ($q4 === 'storm') && ($q6 === 'limo')){

$query = new WP_Query( $args3 );
if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

     the_post_thumbnail('thumbnail');

endwhile;
    endif;      
}
//fourth loop
$args4 = array(
  'post_type' => 'product',
  'posts_per_page' => -1,
  'tax_query' => array(
    'relation' => 'AND',
    array(
     'taxonomy' => 'product_cat',
     'field' => 'slug',
     'terms' => 'family-fanatic'
    ),
//    array(
//     'taxonomy' => 'product_cat',
//     'field' => 'slug',
//     'terms' => 'cheap-and-cheerful'
//    )
  )
);
 //The family-fanatic
 if (($qs ==='movie') && ($q2 === 'kids')&& ($q6 === 'volvo')){

$query = new WP_Query( $args4 );
if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

     the_post_thumbnail('thumbnail');

endwhile;
    endif;      
}
//fifth loop
//fourth loop
$args4 = array(
  'post_type' => 'product',
  'posts_per_page' => -1,
  'tax_query' => array(
    'relation' => 'AND',
    array(
     'taxonomy' => 'product_cat',
     'field' => 'slug',
     'terms' => 'family-fanatic'
    ),
//    array(
//     'taxonomy' => 'product_cat',
//     'field' => 'slug',
//     'terms' => 'cheap-and-cheerful'
//    )
  )
);
 //The romantic
 if (($qs ==='Dinner-show') && ($q5 === 'cruiser')){

$query = new WP_Query( $args4 );
if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

     the_post_thumbnail('thumbnail');

endwhile;
    endif;      
}
Nofel
fuente
¿Cuál es la diferencia con su pregunta anterior ?
fuxia
@toscho la diferencia es que en esa Q. hubo preguntas sobre la refactorización, pero ahora estoy preguntando sobre la optimización del código junto con wp the loop o hacerlo en matriz.
Nofel

Respuestas:

18

Su pregunta no es realmente sobre WordPress, es más sobre PHP y refactorización. Pero vemos mucho código malo aquí, y el patrón que explicaré a continuación (MVC) podría ayudar a muchos otros desarrolladores, así que decidí escribir una pequeña respuesta. Tenga en cuenta que hay un sitio dedicado para tales preguntas en nuestra red: Revisión de código . Desafortunadamente, muy pocos desarrolladores de WordPress están activos allí.


Cómo refactorizar el código

  1. Eliminar código inútil. Embellecer el resto.
  2. Encuentre todas las expresiones repetitivas y cree rutinas (funciones o clases) para abstraerlas y encapsularlas.
  3. Separar el manejo de datos, el modelo (almacenar, buscar, conversión, interpretación), de salida, la vista (HTML, CSV, lo que sea).

1. Eliminar el código inútil. Embellecer el resto.

La salida

Tienes este fragmento de repetición:

if( $query->have_posts()) : while( $query->have_posts() ) : $query->the_post();

the_post_thumbnail('thumbnail');

endwhile;
endif;

Ejecutas el bastante caro the_post()cada vez para obtener la miniatura de la publicación. Pero eso no es necesario, solo puede llamar:

echo get_the_post_thumbnail( $post_id, 'post-thumbnail' );

La consulta

Entonces, todo lo que necesita es la ID de publicación, y está disponible sin llamar the_post(). Aún mejor: puede restringir la consulta para obtener solo los ID.

Un simple ejemplo:

$post_ids = array();
$args     = array(
    'post_type'      => 'post',
    'posts_per_page' => 10,
    'fields'         => 'ids'
);
$query    = new WP_Query( $args );

if ( ! empty ( $query->posts ) )
    $post_ids = $query->posts; // just the post IDs

Ahora tiene las ID y puede escribir:

foreach ( $post_ids as $post_id )
    echo get_the_post_thumbnail( $post_id, 'post-thumbnail' );

Sin gastos generales, su código ya es más rápido y fácil de leer.

La sintaxis

Tenga en cuenta cómo alineé el =? Esto ayuda a comprender el código, porque la mente humana está especializada en el reconocimiento de patrones. Apoye eso y podemos hacer cosas increíbles. Crea un desastre y nos quedamos atrapados muy rápido.

Esta es también la razón por la que eliminé endwhiley endif. La sintaxis alternativa es desordenada y difícil de leer. Además, hace que trabajar en un IDE mucho más difícil: doblar y saltar desde el principio hasta el final de una expresión es más fácil con llaves.

Los valores por defecto

Su $argsmatriz tiene algunos campos que usa en todas partes. Cree una matriz predeterminada y escriba esos campos solo una vez :

$args = array(
    'post_type'      => 'product',
    'posts_per_page' => 100,
    'fields'         => 'ids',
    'tax_query'      => array(
        array(
            'taxonomy' => 'product_cat',
            'field'    => 'slug',
        )
    )
);

Nuevamente, observe la alineación. Y observe también cómo cambié el posts_per_pagevalor. Nunca pidas-1 . ¿Qué sucede cuando hay un millón de publicaciones coincidentes? No desea eliminar la conexión de su base de datos cada vez que se ejecuta esta consulta, ¿verdad? ¿Y quién debería leer todas estas publicaciones? Siempre establezca un límite razonable.

Ahora todo lo que tienes que cambiar es el campo $args[ 'tax_query' ][ 'terms' ] . Lo cubriremos en un momento.

2. Encuentra todas las expresiones repetitivas y crea rutinas

Ya limpiamos un código repetitivo, ahora la parte difícil: la evaluación de los parámetros POST. Obviamente, ha realizado algunas etiquetas como resultado de algunos parámetros. Sugiero cambiarles el nombre a algo más fácil de entender, pero por ahora trabajaremos con su esquema de nombres.

Separe estos grupos del resto, cree una matriz que pueda administrar más adelante por separado:

$groups = array(
    'fashion-follower' => array(
        'q1' => 'party',
        'q2' => 'clothes',
        'q3' => 'shopping',
        'q4' => FALSE,
        'q5' => 'sunbathing',
        'q6' => 'mini',
    ),
    'the-homemaker' => array(
        'q1' => 'drink',
        'q2' => 'candles',
        'q3' => 'house',
        'q4' => 'diy',
        'q5' => FALSE,
        'q6' => FALSE,
    )
);

Para completar el termscampo que falta en su matriz predeterminada, recorra la $groupsmatriz hasta encontrar una coincidencia:

function get_query_term( $groups )
{
    foreach ( $groups as $term => $values )
    {
        if ( compare_group_values( $values ) )
            return $term;
    }

    return FALSE;
}

function compare_group_values( $values )
{
    foreach ( $values as $key => $value )
    {
        // Key not sent, but required
        if ( empty ( $_POST[ $key ] ) and ! empty ( $value ) )
            return FALSE;

        // Key sent, but wrong value
        if ( ! empty ( $_POST[ $key ] ) and $_POST[ $key ] !== $value )
            return FALSE;
    }

    // all keys matched the required values
    return TRUE;
}

Separé incluso la ejecución de la lista de términos y la comparación de los valores, porque estas son operaciones diferentes. Cada parte de su código debe hacer una sola cosa, y debe mantener el nivel de sangría plano para una mejor legibilidad.

Ahora que tenemos todas las partes, peguémoslas juntas.

3. Organización: separe el modelo de la vista

Cuando escribí modelo y vista , tenía algo en mente: el enfoque MVC. Es sinónimo de Model View Controller , un patrón bien conocido para organizar componentes de software. La parte que faltaba hasta ahora era el controlador, veremos cómo lo usamos más adelante.

Dijiste que no sabes mucho sobre PHP, así que espero que sepas más sobre el resultado. :) Comencemos con eso:

class Thumbnail_List
{
    protected $source;

    public function set_source( Post_Collector_Interface $source )
    {
        $this->source = $source;
    }

    public function render()
    {
        $post_ids = $this->source->get_post_ids();

        if ( empty ( $post_ids ) or ! is_array( $post_ids ) )
            return print 'Nothing found';

        foreach ( $post_ids as $post_id )
            echo get_the_post_thumbnail( $post_id, 'post-thumbnail' );
    }
}

Agradable y simple: tenemos dos métodos: uno para establecer la fuente de nuestras ID de publicaciones, otro para representar las miniaturas.

Quizás te preguntes qué Post_Collector_Interfacees esto . Llegamos a eso en un momento.

Ahora la fuente de nuestra vista, el modelo.

class Post_Collector implements Post_Collector_Interface
{
    protected $groups = array();

    public function set_groups( Array $groups )
    {
        $this->groups = $groups;
    }

    public function get_post_ids()
    {
        $term = $this->get_query_term();

        if ( ! $term )
            return array();

        return $this->query( $term );
    }

    protected function query( $term )
    {
        $args = array(
            'post_type'      => 'product',
            'posts_per_page' => 100,
            'fields'         => 'ids',
            'tax_query'      => array(
                array(
                    'taxonomy' => 'product_cat',
                    'field'    => 'slug',
                    'terms'    => $term
                )
            )
        );

        $query = new WP_Query( $args );

        if ( empty ( $query->posts ) )
            return array();

        return $query->posts;
    }

    protected function get_query_term()
    {
        foreach ( $this->groups as $term => $values )
        {
            if ( compare_group_values( $values ) )
                return $term;
        }

        return FALSE;
    }

    protected function compare_group_values( $values )
    {
        foreach ( $values as $key => $value )
        {
            // Key not sent, but required
            if ( empty ( $_POST[ $key ] ) and ! empty ( $value ) )
                return FALSE;

            // Kent sent, but wrong value
            if ( ! empty ( $_POST[ $key ] ) and $_POST[ $key ] !== $value )
                return FALSE;
        }

        // all keys matched the required values
        return TRUE;
    }
}

Esto ya no es tan trivial, pero ya teníamos la mayor parte. Los protected métodos (funciones) no son accesibles desde el exterior, porque los necesitamos solo para la lógica interna.

Los publicmétodos son simples: el primero obtiene nuestra $groupmatriz de arriba, el segundo devuelve una matriz de ID de publicación. Y de nuevo nos encontramos con este dudoso Post_Collector_Interface.

Una interfaz es un contrato . Puede ser firmado (implementado) por clases. Que requiere una interfaz, como nuestra clase Thumbnail_Listhace, mediante: la clase espera alguna otra clase con estos métodos públicos.

Construyamos esa interfaz. Es realmente simple:

interface Post_Collector_Interface
{
    public function set_groups( Array $groups );

    public function get_post_ids();
}

Sí, eso es todo. Código fácil, ¿no?

Lo que hicimos aquí: hicimos nuestra opinión Thumbnail_Listindependiente de una clase concreta, mientras que todavía podemos confiar en los métodos de la clase que obtuvimos $source. Si cambia de opinión más adelante, puede escribir una nueva clase para obtener los ID de las publicaciones o usar una con valores fijos. Mientras implemente la interfaz, la vista estará satisfecha. Incluso puede probar la vista ahora con un objeto simulado:

class Mock_Post_Collector implements Post_Collector_Interface
{
    public function set_groups( Array $groups ) {}

    public function get_post_ids()
    {
        return array ( 1 );
    }
}

Esto es muy útil cuando quieres probar la vista. No desea probar ambas clases concretas juntas, porque no vería de dónde proviene un error. El objeto simulado es demasiado simple para errores, ideal para pruebas unitarias.

Ahora tenemos que combinar nuestras clases de alguna manera. Aquí es donde el controlador entra en escena.

class Thumbnail_Controller
{
    protected $groups = array(
        'fashion-follower' => array(
            'q1' => 'party',
            'q2' => 'clothes',
            'q3' => 'shopping',
            'q4' => FALSE,
            'q5' => 'sunbathing',
            'q6' => 'mini',
        ),
        'the-homemaker' => array(
            'q1' => 'drink',
            'q2' => 'candles',
            'q3' => 'house',
            'q4' => 'diy',
            'q5' => FALSE,
            'q6' => FALSE,
        )
    );
    public function __construct()
    {
        // not a post request
        if ( 'POST' !== $_SERVER[ 'REQUEST_METHOD' ] )
            return;

        // set up the model
        $model = new Post_Collector;
        $model->set_groups( $this->groups );

        // prepare the view
        $view = new Thumbnail_List;
        $view->set_source( $model );

        // finally render the tumbnails
        $view->render();
    }
}

El controlador es la única parte real de una aplicación; el modelo y la vista pueden reutilizarse aquí y allá, incluso en partes completamente diferentes. Pero el controlador existe solo para este único propósito, es por eso que ponemos el$group aquí.

Y ahora solo tienes que hacer una cosa:

// Let the dogs out!
new Thumbnail_Controller;

Llame a esta línea donde necesite la salida.

Puede encontrar todo el código de esta respuesta en esta esencia en GitHub .

fuxia
fuente
66
¿Cuál es el número ISBN de esa respuesta?
Kaiser
Respuesta del libro de texto: p
Manny Fleurmond
1
"Nunca pidas -1". Yo diría: pon un filtro -1para que los usuarios con sitios enormes puedan cambiarlo si es necesario.
chrisguitarguy
@chrisguitarguy Prefiero establecer valores predeterminados seguros y permitir que los usuarios que necesiten -1agregar eso por filtro. Lo cual ya es posible con un filtro en la consulta.
fuxia