Como criar uma abstração flexível para WP_Query?

8

Minha pergunta é sobre php, mas envolve wordpress como eu estou criando um plugin. O caso é que eu tenho 5 perguntas, cada pergunta tem 6 opções e uma opção para escolher. Agora a pessoa selecionaria qualquer opção entre cada uma ou apenas algumas. Eu criei a condição if que agora está me deixando louca, já que ela passou muito tempo e vai além, como quase 100 combinações serão feitas. Eu não gostaria disso, eu sei que existe uma maneira de matriz multidimensional, mas não sou um especialista em plugins ou php para wordpress. então, se alguém puder resolver isso para mim.

$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
fonte
Qual é a diferença para sua pergunta anterior ?
fuxia
@toscho a diferença é que, naquele Q., havia uma pergunta sobre refatoração, mas agora estou perguntando sobre como otimizar o código junto com o loop wp ou com a execução no array.
Nofel 29/08/2013

Respostas:

18

Sua pergunta não é realmente sobre WordPress, é mais sobre PHP e refatoração. Mas vemos muito código ruim aqui, e o padrão que explicarei abaixo (MVC) pode ajudar muitos outros desenvolvedores, então decidi escrever uma pequena resposta. Lembre-se de que existe um site dedicado a essas perguntas em nossa rede: Revisão de código . Infelizmente, muito poucos desenvolvedores do WordPress estão ativos lá.


Como refatorar código

  1. Remova o código inútil. Embeleze o resto.
  2. Encontre todas as expressões repetidas e crie rotinas (funções ou classes) para abstrair e encapsular essas.
  3. Separe a manipulação de dados, o modelo (armazenar, buscar, conversão, interpretação), da saída, a visualização (HTML, CSV, qualquer que seja).

1. Remova o código inútil. Embeleze o resto.

A saída

Você tem este trecho repetido:

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

the_post_thumbnail('thumbnail');

endwhile;
endif;

Você executa o bastante caro the_post()cada vez para obter a miniatura da postagem. Mas isso não é necessário, basta ligar para:

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

A pergunta

Então, tudo que você precisa é o ID da postagem, e isso está disponível sem ligar the_post(). Ainda melhor: você pode restringir a consulta para buscar apenas os IDs.

Um exemplo simples:

$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

Agora você tem os IDs e pode escrever:

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

Sem custos adicionais, seu código já é mais rápido e fácil de ler.

A sintaxe

Observe como eu alinhei o =? Isso ajuda a entender o código, porque a mente humana é especializada em reconhecimento de padrões. Apoie isso e podemos fazer coisas incríveis. Crie uma bagunça, e ficamos presos muito rápido.

Esta é também a razão pela qual eu removi endwhilee endif. A sintaxe alternativa é confusa e difícil de ler. Além disso, torna o trabalho em um IDE muito mais difícil: dobrar e pular do início ao fim de uma expressão é mais fácil com aparelhos.

Os valores padrão

Sua $argsmatriz possui alguns campos que você usa em qualquer lugar. Crie uma matriz padrão e escreva esses campos apenas uma vez :

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

Mais uma vez, observe o alinhamento. E observe também como mudei o posts_per_pagevalor. Nunca peça-1 . O que acontece quando há um milhão de postagens correspondentes? Você não deseja eliminar sua conexão com o banco de dados toda vez que essa consulta for executada, não é? E quem deve ler todos esses posts? Sempre defina um limite razoável.

Agora tudo que você precisa mudar é o campo $args[ 'tax_query' ][ 'terms' ]. Nós vamos cobrir isso em um momento.

2. Encontre todas as expressões repetidas e crie rotinas

Já limpamos alguns códigos repetidos, agora a parte mais difícil: a avaliação dos parâmetros do POST. Obviamente, você criou alguns rótulos como resultado de alguns parâmetros. Sugiro renomeá-los para algo mais fácil de entender, mas, por enquanto, trabalharemos com seu esquema de nomes.

Separe esses grupos dos demais, crie uma matriz que você possa gerenciar posteriormente separadamente:

$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 preencher o termscampo ausente na matriz padrão, você percorre a $groupsmatriz até encontrar uma correspondência:

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;
}

Separei até a execução da lista de termos e a comparação dos valores, porque essas são operações diferentes. Cada parte do seu código deve fazer apenas uma coisa, e você deve manter o nível de indentação plano para melhor legibilidade.

Agora temos todas as partes, vamos juntá-las.

3. Organização: separe o modelo da visualização

Quando escrevi model e view , eu tinha algo em mente: a abordagem MVC. Representa o Model View Controller , um padrão conhecido para organizar componentes de software. A parte que faltava até agora era o controlador, veremos como usá-lo mais tarde.

Você disse que não sabe muito sobre PHP, então espero que saiba mais sobre a saída. :) Vamos começar com isso:

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' );
    }
}

Agradável e simples: temos dois métodos: um para definir a origem dos nossos IDs de postagem, um para renderizar as miniaturas.

Você pode se perguntar o que Post_Collector_Interfaceé isso . Chegamos a isso em um momento.

Agora, a fonte da nossa visão, o 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;
    }
}

Isso não é mais tão trivial, mas já tínhamos a maioria das partes. Os protected métodos (funções) não são acessíveis externamente, porque precisamos deles apenas para a lógica interna.

Os publicmétodos são simples: o primeiro obtém nossa $groupmatriz de cima, o segundo retorna uma matriz de IDs de postagem. E novamente encontramos esse duvidoso Post_Collector_Interface.

Uma interface é um contrato . Pode ser assinado (implementado) por classes. Exigir uma interface, como a nossa classe Thumbnail_List, significa: a classe espera alguma outra classe com esses métodos públicos.

Vamos construir essa interface. É realmente simples:

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

    public function get_post_ids();
}

Sim, isso é tudo. Código fácil, não é?

O que fizemos aqui: tornamos nossa visão Thumbnail_Listindependente de uma classe concreta, enquanto ainda podemos confiar nos métodos da classe que obtivemos $source. Se você mudar de idéia mais tarde, poderá escrever uma nova classe para buscar os IDs de postagem ou usar uma com valores fixos. Contanto que você implemente a interface, a visualização será satisfeita. Você pode até testar a exibição agora com um objeto simulado:

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

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

Isso é muito útil quando você deseja testar a exibição. Você não deseja testar as duas classes concretas juntas, porque não verá de onde vem um erro. O objeto simulado é muito simples para erros, ideal para testes de unidade.

Agora temos que combinar nossas aulas de alguma forma. É aqui que o controlador entra no palco.

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();
    }
}

O controlador é a única parte real real de uma aplicação; o modelo e a vista podem ser reutilizados aqui e ali, mesmo em partes completamente diferentes. Mas o controlador existe apenas para esse propósito único, é por isso que colocamos $groupaqui.

E agora você precisa fazer apenas uma coisa:

// Let the dogs out!
new Thumbnail_Controller;

Ligue para essa linha sempre que precisar da saída.

Você pode encontrar todo o código desta resposta nesta lista no GitHub .

fuxia
fonte
6
Qual é o número do ISBN dessa resposta?
Kaiser #
Resposta do livro didático de fato: p
Manny Fleurmond
1
"Nunca peça -1." Eu diria: coloque um filtro -1para que os usuários com sites enormes possam alterá-lo, se necessário.
chrisguitarguy
@chrisguitarguy Prefiro definir padrões seguros e permitir que os usuários que precisem -1adicionem isso por filtro. O que já é possível com um filtro na consulta.
fuxia