Comment créer une abstraction flexible pour WP_Query?


8

Ma question concerne php mais elle implique wordpress car je crée un plugin. Le cas est que j'ai 5 questions, chaque question a 6 choix et un choix à choisir parmi chacun. Maintenant, la personne sélectionnerait n'importe quel choix parmi chacun ou seulement quelques-uns. J'ai créé la condition if qui me rend maintenant fou, car elle est allée trop longtemps et ira plus loin, comme près de 100 combinaisons seront faites. Je ne voudrais pas cela, je sais qu'il existe un moyen de tableau multidimensionnel mais je ne suis pas un expert en plugin ou en php pour wordpress. donc si quelqu'un peut le trier pour moi.

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

Quelle est la différence avec votre question précédente ?
fuxia

@toscho la différence est que dans ce Q. il y avait une question de refactoring mais maintenant je demande sur l'optimisation du code avec wp la boucle ou le faire en tableau.
Nofel

Réponses:


18

Votre question ne concerne pas vraiment WordPress, elle concerne plutôt PHP et le refactoring. Mais nous voyons tellement de mauvais code ici, et le modèle que j'expliquerai ci-dessous (MVC) pourrait aider de nombreux autres développeurs, j'ai donc décidé d'écrire une petite réponse. Gardez à l'esprit qu'il existe un site dédié à ces questions dans notre réseau: Code Review . Malheureusement, très peu de développeurs WordPress y sont actifs.


Comment refactoriser le code

  1. Supprimez le code inutile. Embellissez le reste.
  2. Trouvez toutes les expressions répétitives et créez des routines (fonctions ou classes) pour les résumer et les encapsuler.
  3. Séparez la gestion des données, le modèle (stockage, extraction, conversion, interprétation), de la sortie, la vue (HTML, CSV, peu importe).

1. Supprimez le code inutile. Embellissez le reste.

Le résultat

Vous avez cet extrait répété:

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

the_post_thumbnail('thumbnail');

endwhile;
endif;

Vous exécutez le plutôt cher à the_post()chaque fois pour obtenir la miniature du message. Mais ce n'est pas nécessaire, vous pouvez simplement appeler:

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

La requête

Donc, tout ce dont vous avez besoin est l'ID du post, et il est disponible sans appel the_post(). Encore mieux: vous pouvez restreindre la requête pour récupérer uniquement les ID.

Un exemple simple:

$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

Vous avez maintenant les identifiants et vous pouvez écrire:

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

Pas de frais généraux, votre code est déjà plus rapide et plus facile à lire.

La syntaxe

Notez comment j'ai aligné le =? Cela aide à comprendre le code, car l'esprit humain est spécialisé dans la reconnaissance des formes. Soutenez cela et nous pouvons faire des choses incroyables. Créez un gâchis et nous sommes coincés très rapidement.

C'est aussi la raison pour laquelle j'ai supprimé endwhileet endif. La syntaxe alternative est désordonnée et difficile à lire. De plus, cela rend le travail dans un IDE beaucoup plus difficile: le pliage et le saut du début à la fin d'une expression sont plus faciles avec des accolades.

Les valeurs par défaut

Votre $argstableau contient des champs que vous utilisez partout. Créez un tableau par défaut et écrivez ces champs une seule fois :

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

Encore une fois, notez l'alignement. Et notez aussi comment j'ai changé la posts_per_pagevaleur. Ne demandez jamais-1 . Que se passe-t-il lorsqu'il y a un million de messages correspondants? Vous ne voulez pas tuer votre connexion à la base de données à chaque exécution de cette requête, n'est-ce pas? Et qui devrait lire tous ces messages? Fixez toujours une limite raisonnable.

Il ne vous reste plus qu'à changer le champ $args[ 'tax_query' ][ 'terms' ]. Nous couvrirons cela dans un instant.

2. Trouvez toutes les expressions répétitives et créez des routines

Nous avons déjà nettoyé du code répétitif, maintenant la partie difficile: l'évaluation des paramètres POST. De toute évidence, vous avez créé des étiquettes à la suite de certains paramètres. Je suggère de les renommer en quelque chose de plus facile à comprendre, mais pour l'instant nous allons travailler avec votre schéma de nommage.

Séparez ces groupes des autres, créez un tableau que vous pourrez gérer ultérieurement séparément:

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

Pour remplir le termschamp manquant dans votre tableau par défaut, vous parcourez le $groupstableau jusqu'à ce que vous trouviez une correspondance:

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

J'ai séparé même le parcours de la liste de termes et la comparaison des valeurs, car ce sont des opérations différentes. Chaque partie de votre code ne devrait faire qu'une seule chose, et vous devez maintenir le niveau d'indentation à plat pour une meilleure lisibilité.

Maintenant que nous avons toutes les pièces, collons-les ensemble.

3. Organisation: séparez le modèle de la vue

Quand j'ai écrit modèle et vue , j'avais quelque chose en tête: l'approche MVC. Il signifie Model View Controller , un modèle bien connu pour organiser les composants logiciels. La partie manquante jusqu'à présent était le contrôleur, nous verrons comment nous l'utiliserons plus tard.

Vous avez dit que vous ne connaissiez pas grand-chose à PHP, j'espère donc que vous en savez plus sur la sortie. :) Commençons par ça:

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

Agréable et simple: nous avons deux méthodes: une pour définir la source de nos identifiants de publication, une pour rendre les vignettes.

Vous vous demandez peut-être ce que Post_Collector_Interfacec'est. Nous y arrivons dans un instant.

Maintenant, la source de notre point de vue, le modèle.

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

Ce n'est plus si trivial, mais nous avions déjà la plupart des pièces. Les protected méthodes (fonctions) ne sont pas accessibles de l'extérieur, car nous n'en avons besoin que pour la logique interne.

Les publicméthodes sont simples: la première obtient notre $grouptableau par le haut, la seconde renvoie un tableau d'ID de publication. Et encore une fois, nous rencontrons ce douteux Post_Collector_Interface.

Une interface est un contrat . Il peut être signé (implémenté) par les classes. Exiger une interface, comme le fait notre classe Thumbnail_List, signifie: la classe attend une autre classe avec ces méthodes publiques.

Construisons cette interface. C'est vraiment simple:

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

    public function get_post_ids();
}

Ouais, c'est tout. Code facile, n'est-ce pas?

Ce que nous avons fait ici: nous avons rendu notre vision Thumbnail_Listindépendante d'une classe concrète alors que nous pouvons toujours nous fier aux méthodes de la classe que nous avons obtenue $source. Si vous changez d'avis plus tard, vous pouvez écrire une nouvelle classe pour récupérer les ID de publication ou en utiliser une avec des valeurs fixes. Tant que vous implémentez l'interface, la vue sera satisfaite. Vous pouvez même tester la vue maintenant avec un objet factice:

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

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

Ceci est très utile lorsque vous souhaitez tester la vue. Vous ne voulez pas tester les deux classes concrètes ensemble, car vous ne verriez pas d'où vient une erreur. L'objet simulé est trop simple pour les erreurs, idéal pour les tests unitaires.

Maintenant, nous devons combiner nos classes en quelque sorte. C'est là que le contrôleur entre en scène.

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

Le contrôleur est la seule partie vraiment unique d'une application; le modèle et la vue peuvent être réutilisés ici et là, même dans des parties complètement différentes. Mais le contrôleur existe uniquement pour ce seul but, c'est pourquoi nous mettons le $groupici.

Et maintenant, vous devez faire une seule chose:

// Let the dogs out!
new Thumbnail_Controller;

Appelez cette ligne partout où vous avez besoin de la sortie.

Vous pouvez trouver tout le code de cette réponse dans cet essentiel sur GitHub .


6
Quel est le numéro ISBN de cette réponse?
kaiser

Réponse du manuel: p
Manny Fleurmond

1
"Ne demandez jamais -1." Je dirais: mettez un filtre pour -1que les utilisateurs avec des sites énormes puissent le changer s'ils en ont besoin.
chrisguitarguy

@chrisguitarguy Je préfère définir des valeurs par défaut sûres et laisser les utilisateurs qui en ont besoin -1ajouter par filtre. Ce qui est déjà possible avec un filtre sur la requête.
fuxia
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.