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


8

私の質問はphpについてですが、プラグインを作成しているため、ワードプレスが関係しています。ケースは私が5つの質問を持っているということです、各質問は6つの選択肢とそれぞれから選択する1つの選択肢を持っています。今、人はそれぞれまたはいくつかから任意の選択肢を選択します。私は、100近くの組み合わせが作成されるように、長くなりすぎてさらに処理が進むため、現在私を怒らせている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;      
}

以前の質問との違いは何ですか?
fuxia

@toschoの違いは、そのQではリファクタリングについての質問がありましたが、今はループのwpと共にコードを最適化するか、配列で実行することを求めています。
Nofel 2013

回答:


18

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


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

  1. 不要なコードを削除します。残りを美しく。
  2. すべての繰り返し式を見つけ、それらを抽象化してカプセル化するルーチン(関数またはクラス)を作成します。
  3. データ処理、モデル(保存、フェッチ、変換、解釈)、出力、ビュー(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 だけをフェッチするようにクエリを制限できます。

簡単な例:

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

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

構文

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

これは私が削除した理由でもendwhileありendifます。代替構文は汚いと読みにくいです。さらに、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.構成:モデルをビューから分離します

モデルビューを書いたとき、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、を意味しない:クラスが期待し、いくつかの これらのパブリックメソッドを他のクラスを。

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

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

    public function get_post_ids();
}

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

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

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

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

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

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

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

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

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

// Let the dogs out!
new Thumbnail_Controller;

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

この回答のすべてのコードは、GitHubのこの要点で見つけることができます。


6
その回答のISBN番号は何ですか?
カイザー2013

教科書の答えは確かに:p
マニーフルールモンド2013

1
「-1を要求しないでください。」私は言うでしょう:-1巨大なサイトを持つユーザーが必要に応じて変更できるようにフィルターをかけます。
chrisguitarguy 2013

@chrisguitarguy安全なデフォルトを設定し、-1フィルターごとにそれを追加する必要があるユーザーに許可します。これは、クエリのフィルターで既に可能です。
fuxia
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.