web-dev-qa-db-fra.com

Comment créer une abstraction flexible pour WP_Query?

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 choisirait n'importe quel choix parmi chacun ou juste quelques-uns. J'ai créé la condition préalable qui me rend fou, car elle dure trop longtemps et ira plus loin, comme si près de 100 combinaisons seraient fabriquées. Je ne voudrais pas cela, je sais qu'il existe un moyen de tableau multidimensionnel mais je ne suis pas un plugin ou un expert 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;      
}
8
Nofel

Votre question ne concerne pas vraiment WordPress, mais plutôt PHP et le refactoring. Mais nous voyons tellement de mauvais code ici, et le motif que je vais expliquer ci-dessous (MVC) pourrait aider de nombreux autres développeurs. J'ai donc décidé d'écrire une petite réponse. N'oubliez pas qu'il existe un site dédié à ce type de questions dans notre réseau: Code Review . Malheureusement, très peu de développeurs WordPress y sont actifs.


Comment refactoriser le code

  1. Supprimer le code inutile. Embellissez le reste.
  2. Recherchez toutes les expressions répétées et créez des routines (fonctions ou classes) pour les résumer et les encapsuler.
  3. Traitement séparé des données, le modèle (magasin, extraction, conversion, interprétation), de la sortie, le 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 la the_post() plutôt coûteuse à chaque fois pour obtenir la vignette 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 de poste, qui est disponible sans appeler the_post(). Mieux encore: vous pouvez limiter la requête à extraire seulement 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

Maintenant vous avez les identifiants et vous pouvez écrire:

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

Pas de surcharge, 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 géniales. Créer un désordre, et nous nous retrouvons très vite coincés.

C'est aussi la raison pour laquelle j'ai supprimé endwhile et endif. La syntaxe alternative est compliquée et difficile à lire. De plus, cela rend le travail dans un IDE beaucoup plus difficile: plier et sauter du début à la fin d'une expression est plus facile avec des accolades.

Les valeurs par défaut

Votre tableau $args contient des champs que vous utilisez partout. Créez un tableau par défaut et écrivez ces champs simplement une 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 valeur posts_per_page. Ne jamais demander -1. Que se passe-t-il quand il y a un million de messages correspondants? Vous ne souhaitez pas supprimer votre connexion à la base de données chaque fois que cette requête est exécutée, n'est-ce pas? Et qui devrait lire tous ces messages? Toujours fixer une limite raisonnable.

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

2. Trouver toutes les expressions qui se répètent et créer des routines

Nous avons déjà nettoyé du code répétitif, maintenant la partie la plus difficile: l'évaluation des paramètres POST. De toute évidence, vous avez créé des étiquettes en fonction de certains paramètres. Je suggère de renommer ceux-ci 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 champ terms manquant dans votre tableau par défaut, vous parcourez le tableau $groups 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 même séparé la liste des termes et la comparaison des valeurs, car il s'agit d'opérations différentes. Chaque partie de votre code doit faire une seule chose et vous devez garder 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.

Lorsque j’écrivais modèle et vue, j’avais en tête quelque chose: l’approche MVC. Il signifie Model View Controller, un modèle bien connu d'organisation des composants logiciels. La partie manquante jusqu'ici était le contrôleur, nous verrons comment nous l'utilisons plus tard.

Vous avez dit que vous ne connaissiez pas grand chose à PHP, alors j'espère que vous en saurez plus sur le résultat. :) 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' );
    }
}

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

Vous pourriez vous demander ce qu'est ce Post_Collector_Interface. Nous y arrivons dans un instant.

Maintenant, la source de notre 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 méthodes (fonctions) protected ne sont pas accessibles de l'extérieur, car nous en avons besoin uniquement pour la logique interne.

Les méthodes public sont simples: la première obtient notre tableau $group d’en haut, la seconde renvoie un tableau d’ID de publication. Et encore une fois nous rencontrons ce Post_Collector_Interface douteux.

Une interface est un contrat . Il peut être signé (implémenté) par les classes. Exiger une interface, comme notre classe Thumbnail_List le fait, signifie que la classe attend certains 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();
}

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

Ce que nous avons fait ici: nous avons rendu notre vue Thumbnail_List indépendante d'une classe concrète, alors que nous pouvons toujours compter sur les méthodes de la classe que nous avons obtenues sous la forme $source. Si vous changez d'avis par la suite, vous pouvez écrire une nouvelle classe pour récupérer les ID d'articles ou en utiliser une avec des valeurs fixes. Tant que vous implémenterez l'interface, la vue sera satisfaite. Vous pouvez même tester la vue maintenant avec un objet fictif:

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 voulez tester la vue. Vous ne voulez pas tester les deux classes concrètes ensemble, car vous ne verriez pas d’où provient une erreur. L'objet fictif est trop simple pour les erreurs, idéal pour les tests unitaires.

Nous devons maintenant combiner nos cours d’une manière ou d’une autre. C'est ici 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 et unique partie 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 n’existe que pour ce seul but, c’est pourquoi nous avons mis le $group ici.

Et maintenant, vous devez faire juste une 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 ce Gist sur GitHub .

18
fuxia