Come creare un'astrazione flessibile per WP_Query?


8

La mia domanda riguarda php ma coinvolge wordpress mentre sto creando un plugin. Il caso è che ho 5 domande, ogni domanda ha 6 scelte e una scelta tra cui scegliere. Ora la persona selezionerebbe qualsiasi scelta tra ciascuna o solo poche. Ho creato la condizione if che ora mi sta facendo impazzire, poiché è andata troppo a lungo e farà di più, come se fossero fatte quasi 100 combinazioni. Non lo vorrei, so che esiste un modo di array multidimensionale, ma non sono un esperto di plugin o php per wordpress. quindi se qualcuno può ordinarlo per me.

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

Qual è la differenza con la tua domanda precedente ?
fuxia

@toscho la differenza è che in quella Q. c'era una domanda sul refactoring ma ora sto chiedendo di ottimizzare il codice insieme a wp il loop o averlo fatto in array.
Nofel,

Risposte:


18

La tua domanda non riguarda davvero WordPress, è più di PHP e refactoring. Ma vediamo così tanto codice errato qui, e il modello che spiegherò di seguito (MVC) potrebbe aiutare molti altri sviluppatori, quindi ho deciso di scrivere una piccola risposta. Tieni presente che esiste un sito dedicato per tali domande nella nostra rete: Revisione del codice . Sfortunatamente, pochissimi sviluppatori WordPress sono attivi lì.


Come refactificare il codice

  1. Rimuovi il codice inutile. Abbellisci il resto.
  2. Trova tutte le espressioni ripetute e crea routine (funzioni o classi) per astrarre e incapsulare quelle.
  3. Separare la gestione dei dati, il modello (archivio, recupero, conversione, interpretazione), dall'output, dalla vista (HTML, CSV, qualunque cosa).

1. Rimuovere il codice inutile. Abbellisci il resto.

Il risultato

Hai questo frammento ripetuto:

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

the_post_thumbnail('thumbnail');

endwhile;
endif;

Corri piuttosto costoso the_post()ogni volta per ottenere l'anteprima del post. Ma non è necessario, puoi semplicemente chiamare:

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

La domanda

Quindi tutto ciò che serve è l'ID post e questo è disponibile senza chiamare the_post(). Ancora meglio: puoi limitare la query al solo recupero gli ID.

Un semplice esempio:

$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

Ora hai gli ID e puoi scrivere:

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

Nessun sovraccarico, il tuo codice è già più veloce e più facile da leggere.

La sintassi

Nota come ho allineato il = ? Questo aiuta a comprendere il codice, perché la mente umana è specializzata nel riconoscimento di schemi. Sostienilo e possiamo fare cose fantastiche. Crea un pasticcio e restiamo bloccati molto velocemente.

Questo è anche il motivo per cui ho rimosso endwhilee endif. La sintassi alternativa è disordinata e difficile da leggere. Inoltre, fa funzionare in un IDE molto più difficile : piegare e saltare dall'inizio alla fine di un'espressione è più facile con le parentesi graffe.

I valori predefiniti

Il tuo $argsarray ha alcuni campi che usi ovunque. Crea un array predefinito e scrivi quei campi solo una volta :

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

Ancora una volta, notare l'allineamento. E nota anche come ho cambiato il posts_per_pagevalore. Non chiedere mai-1 . Cosa succede quando ci sono un milione di post corrispondenti? Non vuoi interrompere la connessione al database ogni volta che viene eseguita questa query, vero? E chi dovrebbe leggere tutti questi post? Imposta sempre un limite ragionevole.

Ora tutto ciò che devi cambiare è il campo $args[ 'tax_query' ][ 'terms' ] . Lo tratteremo tra un momento.

2. Trova tutte le espressioni ripetute e crea routine

Abbiamo già ripulito del codice ripetuto, ora la parte difficile: la valutazione dei parametri POST. Ovviamente, hai creato alcune etichette come risultato di alcuni parametri. Suggerisco di rinominarli in qualcosa di più facile da capire, ma per ora lavoreremo con il tuo schema di denominazione.

Separare questi gruppi dal resto, creare un array che è possibile gestire in un secondo momento separatamente:

$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,
    )
);

Per riempire il termscampo mancante nell'array predefinito, esegui l' $groupsarray fino a trovare una corrispondenza:

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

Ho separato anche la corsa attraverso l'elenco dei termini e il confronto dei valori, perché si tratta di operazioni diverse. Ogni parte del codice dovrebbe fare solo una cosa e devi mantenere piatto il livello di rientro per una migliore leggibilità.

Ora abbiamo tutte le parti, mettiamole insieme.

3. Organizzazione: separare il modello dalla vista

Quando ho scritto il modello e la vista , avevo in mente qualcosa: l'approccio MVC. Sta per Model View Controller , un modello ben noto per organizzare i componenti software. La parte mancante finora era il controller, vedremo come lo useremo in seguito.

Hai detto che non sai molto di PHP, quindi spero che tu sappia di più sull'output. :) Cominciamo con quello:

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

Simpatico e semplice: abbiamo due metodi: uno per impostare l'origine dei nostri ID post, uno per rendere le anteprime.

Potresti chiederti di cosa si Post_Collector_Interfacetratta. Ci arriviamo tra un momento.

Ora la fonte per il nostro punto di vista, il modello.

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

Questo non è più così banale, ma avevamo già la maggior parte delle parti. I protected metodi (funzioni) non sono accessibili dall'esterno, perché ne abbiamo bisogno solo per la logica interna.

I publicmetodi sono semplici: il primo ottiene il nostro $grouparray dall'alto, il secondo restituisce un array di ID post. E ancora incontriamo questo dubbio Post_Collector_Interface.

Un'interfaccia è un contratto . Può essere firmato (implementato) da classi. Che richiedono un'interfaccia, come la nostra classe Thumbnail_Listfa, i mezzi: la classe si aspetta qualche altra classe con questi metodi pubblici.

Costruiamo quell'interfaccia. È davvero semplice:

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

    public function get_post_ids();
}

Sì, tutto qui. Codice semplice, vero?

Cosa abbiamo fatto qui: abbiamo reso la nostra visione Thumbnail_Listindipendente da una classe concreta mentre possiamo ancora fare affidamento sui metodi della classe che abbiamo ottenuto $source. Se cambi idea in seguito, puoi scrivere una nuova classe per recuperare gli ID dei post o utilizzarne uno con valori fissi. Finché si implementa l'interfaccia, la vista sarà soddisfatta. Puoi persino testare la vista ora con un oggetto finto:

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

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

Questo è molto utile quando si desidera testare la vista. Non vuoi testare entrambe le classi concrete insieme, perché non vedresti da dove provenga un errore. L'oggetto finto è troppo semplice per gli errori, ideale per i test unitari.

Ora dobbiamo combinare le nostre classi in qualche modo. Qui è dove il controller entra nel 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();
    }
}

Il controller è l'unica vera parte unica di un'applicazione; il modello e la vista potrebbero essere riutilizzati qua e là, anche in parti completamente diverse. Ma il controller esiste solo per questo unico scopo, ecco perché lo abbiamo messo$group qui.

E ora devi fare solo una cosa:

// Let the dogs out!
new Thumbnail_Controller;

Chiama questa linea ovunque sia necessario l'output.

Puoi trovare tutto il codice di questa risposta in questa sintesi su GitHub .


6
Qual è il numero ISBN di quella risposta?
Kaiser

Risposta del libro di testo in effetti: p
Manny Fleurmond,

1
"Non chiedere mai -1." Direi: metti un filtro in -1modo che gli utenti con siti enormi possano cambiarlo se necessario.
chrisguitarguy,

@chrisguitarguy Preferirei impostare valori predefiniti sicuri e consentire agli utenti che hanno bisogno di -1aggiungerlo per filtro. Che è già possibile con un filtro sulla query.
fuxia
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.