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
- Rimuovi il codice inutile. Abbellisci il resto.
- Trova tutte le espressioni ripetute e crea routine (funzioni o classi) per astrarre e incapsulare quelle.
- 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 endwhile
e 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 $args
array 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_page
valore.
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 terms
campo mancante nell'array predefinito, esegui l'
$groups
array 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_Interface
tratta. 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 public
metodi sono semplici: il primo ottiene il nostro $group
array 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_List
fa, 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_List
indipendente 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 .