web-dev-qa-db-ja.com

WP_Query用の柔軟な抽象化を作成する方法

私の質問はphpですが、私はプラグインを作成しているので、それはwordpressを含みます。 5つの質問があり、それぞれの質問に6つの選択肢とそれぞれから1つの選択肢があります。今、人はそれぞれから、またはほんの少しから任意の選択肢を選択します。私は今では私を怒らせているif条件を作りました。私はそれをしたくないでしょう、私は多次元配列の方法があることを知っていますが、私はワードプレスのためのプラグインやPHPの専門家ではありません。だから誰かが私のためにそれをソートすることができれば。

$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

あなたの質問は実際にはWordPressについてではなく、PHPとリファクタリングについてです。しかし、ここには非常に多くの悪いコードがあり、以下で説明するパターン(MVC)は他の多くの開発者に役立つ可能性があるため、少し答えを書くことにしました。ネットワークには、このような質問専用のサイト Code Review があります。残念ながら、そこで活動しているWordPress開発者はほとんどいません。


コードをリファクタリングする方法

  1. 無駄なコードを削除します。残りを美化します。
  2. すべての繰り返し式を検索し、それらを抽象化してカプセル化するルーチン(関数またはクラス)を作成します。
  3. 別のデータ処理、model(ストア、フェッチ、変換、解釈)、出力から、view(HTML、CSVなど)。

1.無駄なコードを削除します。残りを美化します。

出力

この繰り返しスニペットがあります:

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

the_post_thumbnail('thumbnail');

endwhile;
endif;

投稿のサムネイルを取得するたびに、かなり高価なthe_post()を実行します。しかし、それは必要ありません、あなたはただ電話することができます:

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

クエリ

したがって、必要なのは投稿IDのみで、the_post()を呼び出さなくても利用できます。さらに良いことは、IDを取得するjustクエリを制限することができます。

簡単な例:

$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

これでIDが得られ、次のように記述できます。

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

オーバーヘッドはありません。コードはすでに高速で読みやすくなっています。

構文

=の整列方法に注意してください。人間の心はパターン認識に特化されているため、これはコードの理解に役立ちます。それをサポートすれば、素晴らしいことをすることができます。混乱を作成し、私たちは非常に速く立ち往生します。

これは、endwhileendifを削除した理由でもあります。 代替構文 は乱雑で読みにくいです。さらに、 IDEでの作業 がはるかに難しくなります。式の先頭から末尾への折りたたみとジャンプは、ブレースを使用すると簡単になります。

デフォルト値

$args配列には、どこでも使用するいくつかのフィールドがあります。デフォルトの配列を作成し、それらのフィールドに1回だけ書き込みます

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

繰り返しますが、配置に注意してください。また、posts_per_page値を変更した方法にも注意してください。 決して-1を要求しないでください。 100万件の一致する投稿があるとどうなりますか?このクエリを実行するたびにデータベース接続を強制終了したくないですか?そして、これらすべての投稿を誰が読むべきでしょうか?常に適切な制限を設定してください。

変更する必要があるのは、フィールド$args[ 'tax_query' ][ 'terms' ]だけです。これについては後ほど説明します。

2.すべての繰り返し式を見つけてルーチンを作成する

すでにいくつかの繰り返しコードをクリーンアップしましたが、今では難しい部分であるPOSTパラメーターの評価です。明らかに、いくつかのパラメーターの結果としていくつかのラベルを作成しました。これらの名前をわかりやすい名前に変更することをお勧めしますが、ここでは名前付けスキームを使用します。

これらのグループを残りのグループから分離し、後で個別に管理できるアレイを作成します。

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

デフォルトの配列で欠落しているtermsフィールドを埋めるには、一致するものが見つかるまで$groups配列を実行します。

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

これらは異なる操作であるため、用語リストと値の比較を実行することも分離しました。コードのすべての部分で1つのことを行う必要があります。読みやすくするには、インデントレベルをフラットに保つ必要があります。

すべての部品が揃ったので、一緒に貼りましょう。

3.組織:ビューからモデルを分離します

modelviewを書いたとき、MVCアプローチということを念頭に置いていました。これはModel View Controllerの略で、ソフトウェアコンポーネントを整理するためのよく知られたパターンです。これまで欠けていた部分はコントローラーでした。後で使用する方法を確認します。

あなたは、PHPについてあまり知らないので、出力についてもっと知ってほしいと言いました。 :)それから始めましょう:

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

素晴らしく簡単です。2つのメソッドがあります。1つは投稿IDのソースを設定する方法、もう1つはサムネイルをレンダリングする方法です。

このPost_Collector_Interfaceが何であるか疑問に思うかもしれません。すぐにそれを実現します。

ビューのソースであるモデル。

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

これはささいなことではありませんが、すでにほとんどの部分がありました。 protectedメソッド(関数)は外部からはアクセスできません。これは内部ロジックにのみ必要なためです。

publicメソッドは簡単です。最初のメソッドは上から$group配列を取得し、2番目のメソッドは投稿IDの配列を返します。そして再び、この疑わしいPost_Collector_Interfaceに出会います。

インターフェイスはコントラクトです 。クラスによって署名(実装)できます。クラスThumbnail_Listのようにインターフェイスが必要なのは、クラスがsomeこれらのパブリックメソッドを持つ他のクラスを期待していることを意味します。

そのインターフェースを作成しましょう。それは本当に簡単です:

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

    public function get_post_ids();
}

うん、それだけです。簡単なコードですね。

ここで行ったこと:ビューをThumbnail_Listから具象クラスから独立させましたが、$sourceとして取得したクラスのメソッドをrelyまだ使用できます。後で気が変わった場合は、新しいクラスを作成して投稿IDを取得するか、値が固定されたものを使用できます。インターフェイスを実装する限り、ビューは満足されます。モックオブジェクトを使用して、ビューをテストすることもできます。

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

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

これは、ビューをtestする場合に非常に便利です。エラーの原因がわからないため、両方の具象クラスを一緒にテストする必要はありません。モックオブジェクトはエラーに対して単純すぎて、単体テストに最適です。

ここで、クラスを何らかの方法で結合する必要があります。これは、コントローラーがステージに入る場所です。

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

コントローラーは、アプリケーションの本当の唯一の部分です。モデルとビューは、まったく異なる部分であっても、あちこちで再利用される可能性があります。しかし、コントローラーはこの単一の目的のためだけに存在するため、ここに$groupを配置します。

そして今、あなたはただ一つのことをしなければなりません:

// Let the dogs out!
new Thumbnail_Controller;

出力が必要な場合はいつでもこの行を呼び出します。

この回答のすべてのコードは、この GitHubの要点 にあります。

18
fuxia