Wie erstelle ich eine flexible Abstraktion für WP_Query?


8

Meine Frage bezieht sich auf PHP, aber es geht um WordPress, da ich ein Plugin erstelle. Der Fall ist, dass ich 5 Fragen habe, jede Frage hat 6 Auswahlmöglichkeiten und eine Auswahl zur Auswahl. Jetzt würde die Person eine Auswahl aus jeder oder nur wenigen auswählen. Ich habe die if-Bedingung geschaffen, die mich jetzt wütend macht, da sie zu lange gedauert hat und weiter gehen wird, als würden fast 100 Kombinationen gemacht. Ich würde das nicht wollen, ich weiß, dass es einen Weg für mehrdimensionale Arrays gibt, aber ich bin kein Plugin oder PHP-Experte für WordPress. Also, wenn jemand es für mich sortieren kann.

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

Was ist der Unterschied zu Ihrer früheren Frage ?
Fuxia

@toscho der Unterschied ist, dass es in diesem Q. eine Frage zum Refactoring gab, aber jetzt frage ich nach der Optimierung des Codes zusammen mit wp der Schleife oder der Ausführung im Array.
Nofel

Antworten:


18

Bei Ihrer Frage geht es nicht wirklich um WordPress, sondern eher um PHP und Refactoring. Aber wir sehen hier so viel schlechten Code, und das Muster, das ich unten erläutern werde (MVC), könnte vielen anderen Entwicklern helfen. Deshalb habe ich beschlossen, eine kleine Antwort zu schreiben. Beachten Sie, dass es in unserem Netzwerk eine spezielle Website für solche Fragen gibt: Code Review . Leider sind dort nur sehr wenige WordPress-Entwickler aktiv.


So überarbeiten Sie Code

  1. Entfernen Sie nutzlosen Code. Verschönere den Rest.
  2. Finden Sie alle sich wiederholenden Ausdrücke und erstellen Sie Routinen (Funktionen oder Klassen), um diese zu abstrahieren und zu kapseln.
  3. Trennen Sie die Datenverarbeitung, das Modell (Speichern, Abrufen, Konvertieren, Interpretieren) von der Ausgabe, die Ansicht (HTML, CSV, was auch immer).

1. Entfernen Sie unbrauchbaren Code. Verschönere den Rest.

Die Ausgabe

Sie haben dieses sich wiederholende Snippet:

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

the_post_thumbnail('thumbnail');

endwhile;
endif;

Sie führen the_post()jedes Mal das ziemlich teure aus , um das Post-Thumbnail zu erhalten. Aber das wird nicht benötigt, Sie können einfach anrufen:

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

Die Abfrage

Alles, was Sie brauchen, ist die Post-ID, die ohne Anruf verfügbar ist the_post(). Noch besser: Sie können die Abfrage so einschränken, dass nur die IDs abgerufen werden.

Ein einfaches Beispiel:

$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

Jetzt haben Sie die IDs und können schreiben:

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

Kein Overhead, Ihr Code ist bereits schneller und einfacher zu lesen.

Die Syntax

Beachten Sie, wie ich das ausgerichtet habe =? Dies hilft beim Verständnis des Codes, da der menschliche Geist auf die Mustererkennung spezialisiert ist. Unterstützen Sie das und wir können großartige Dinge tun. Erstellen Sie ein Chaos, und wir bleiben sehr schnell stecken.

Dies ist auch der Grund, warum ich endwhileund entfernt habe endif. Die alternative Syntax ist chaotisch und schwer zu lesen. Außerdem wird die Arbeit in einer IDE dadurch erheblich erschwert : Das Falten und Springen vom Anfang bis zum Ende eines Ausdrucks ist mit geschweiften Klammern einfacher.

Die Standardwerte

Ihr $argsArray enthält einige Felder, die Sie überall verwenden. Erstellen Sie ein Standardarray und schreiben Sie diese Felder nur einmal :

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

Beachten Sie auch hier die Ausrichtung. Und beachte auch, wie ich den posts_per_pageWert geändert habe . Fragen Sie niemals danach-1 . Was passiert, wenn es eine Million übereinstimmende Beiträge gibt? Sie möchten Ihre Datenbankverbindung nicht jedes Mal beenden, wenn diese Abfrage ausgeführt wird, oder? Und wer sollte all diese Beiträge lesen? Setzen Sie immer ein angemessenes Limit.

Jetzt müssen Sie nur noch das Feld ändern $args[ 'tax_query' ][ 'terms' ]. Wir werden das gleich behandeln.

2. Finden Sie alle sich wiederholenden Ausdrücke und erstellen Sie Routinen

Wir haben bereits einige sich wiederholende Codes bereinigt, jetzt der schwierige Teil: die Auswertung der POST-Parameter. Offensichtlich haben Sie aufgrund einiger Parameter einige Beschriftungen vorgenommen. Ich schlage vor, diese in etwas umbenennenderes umzubenennen, aber im Moment werden wir mit Ihrem Namensschema arbeiten.

Trennen Sie diese Gruppen vom Rest und erstellen Sie ein Array, das Sie später separat verwalten können:

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

Um das fehlende termsFeld in Ihrem Standardarray zu füllen , durchlaufen Sie das $groupsArray, bis Sie eine Übereinstimmung finden:

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

Ich habe sogar den Durchlauf durch die Termliste und den Vergleich der Werte getrennt, da es sich um unterschiedliche Operationen handelt. Jeder Teil Ihres Codes sollte nur eines tun, und Sie müssen die Einrückungsstufe flach halten, um die Lesbarkeit zu verbessern.

Jetzt haben wir alle Teile, lassen Sie uns sie zusammenkleben.

3. Organisation: Trennen Sie das Modell von der Ansicht

Als ich Modell und Ansicht schrieb , hatte ich etwas im Sinn: den MVC-Ansatz. Es steht für Model View Controller , ein bekanntes Muster zum Organisieren von Softwarekomponenten. Der bisher fehlende Teil war der Controller, wir werden später sehen, wie wir ihn verwenden.

Sie sagten, Sie wissen nicht viel über PHP, also hoffe ich, dass Sie mehr über die Ausgabe wissen. :) Fangen wir damit an:

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

Schön und einfach: Wir haben zwei Methoden: eine zum Festlegen der Quelle für unsere Post-IDs und eine zum Rendern der Miniaturansichten.

Sie fragen sich vielleicht, was das Post_Collector_Interfaceist. Wir kommen gleich dazu.

Nun die Quelle für unsere Ansicht, das Modell.

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

Das ist nicht mehr so ​​trivial, aber wir hatten schon die meisten Teile. Die protected Methoden (Funktionen) sind von außen nicht zugänglich, da wir sie nur für die interne Logik benötigen.

Die publicMethoden sind einfach: Die erste ruft unser $groupArray von oben ab, die zweite gibt ein Array von Post-IDs zurück. Und wieder treffen wir diesen zweifelhaften Post_Collector_Interface.

Eine Schnittstelle ist ein Vertrag . Es kann von Klassen signiert (implementiert) werden. Das Erfordernis einer Schnittstelle, wie es unsere Klasse Thumbnail_Listtut, bedeutet: Die Klasse erwartet eine andere Klasse mit diesen öffentlichen Methoden.

Lassen Sie uns diese Schnittstelle erstellen. Es ist wirklich einfach:

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

    public function get_post_ids();
}

Ja, das ist alles. Einfacher Code, nicht wahr?

Was wir hier gemacht haben: Wir haben unsere Sichtweise Thumbnail_Listunabhängig von einer konkreten Klasse gemacht, während wir uns immer noch auf die Methoden der Klasse verlassen können, als die wir wurden $source. Wenn Sie es sich später anders überlegen, können Sie eine neue Klasse schreiben, um die Post-IDs abzurufen, oder eine mit festen Werten verwenden. Solange Sie die Schnittstelle implementieren, ist die Ansicht zufriedenstellend. Sie können die Ansicht jetzt sogar mit einem Scheinobjekt testen:

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

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

Dies ist sehr nützlich, wenn Sie die Ansicht testen möchten . Sie möchten nicht beide konkreten Klassen zusammen testen, da Sie dann nicht sehen würden, woher ein Fehler kommt. Das Scheinobjekt ist zu einfach für Fehler, ideal für Unit-Tests.

Jetzt müssen wir unsere Klassen irgendwie kombinieren. Hier betritt der Controller die Bühne.

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

Der Controller ist der einzig einzigartige Teil einer Anwendung. Das Modell und die Ansicht können hier und da auch in völlig anderen Teilen wiederverwendet werden. Aber der Controller existiert nur für diesen einen Zweck, deshalb setzen wir den $grouphier.

Und jetzt müssen Sie nur noch eines tun:

// Let the dogs out!
new Thumbnail_Controller;

Rufen Sie diese Leitung an, wo immer Sie die Ausgabe benötigen.

Sie finden den gesamten Code aus dieser Antwort in dieser Übersicht auf GitHub .


6
Wie lautet die ISBN-Nummer dieser Antwort?
Kaiser

Lehrbuch Antwort in der Tat: p
Manny Fleurmond

1
"Frag niemals nach -1." Ich würde sagen: Setzen Sie einen Filter auf, -1damit Benutzer mit großen Websites ihn bei Bedarf ändern können.
Chrisguitarguy

@chrisguitarguy Ich möchte lieber sichere Standardeinstellungen festlegen und Benutzer, die diese benötigen -1, per Filter hinzufügen lassen. Was mit einem Filter auf der Abfrage bereits möglich ist.
Fuxia
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.