web-dev-qa-db-ja.com

それぞれ独自のメタクエリを持つ複数の投稿タイプをクエリする

私は2つの投稿タイプがあります:productsproduct_variationsと言いましょう。 pre_get_postsでクエリを変更して、すべてのproductをmeta_keys "_visible" = "true"および"_available" = "true"に、そしてすべてのproduct_variationsをmeta_key "_featured" = "true"にすることができます。

Product_variationsはmeta_keys "_visible"/"_available"を持っていません、そしてその逆も同様です、それでまっすぐにAND meta_queryは結果を返しません。 2つのフィールドが一方の投稿タイプに対してOR関連し、もう一方の投稿タイプに対してAND関連しているため、OR関連はまったく正しくありません。

これを少しだけ視覚的にするために、書き出すのがどれほど複雑かを実感しました。

**product**        
_visible = true      
_available = true

**product_variation**      
_featured = true

これはクエリ引数で可能ですか。
これはposts_whereを通じて可能ですか?
別のクエリを実行してから、どういうわけか結果をマージする必要がありますか?

4
helgatheviking

これはクエリ引数で可能ですか。

私はそうは思わない。

これは'posts_where'を通じて可能ですか?

おそらく'posts_*'だけでなくsome'posts_where'フィルタを使っても可能です。あるいは、 'posts_request' filterを使用してクエリを完全にオーバーライドすることもできます。

別のクエリを実行してから結果を何らかの方法でマージする必要がありますか。

それが最も単純な選択であり、最もカスタマイズが容易なもの、例えば製品に対する制限および製品のバリエーションに対する異なる制限を使用することが容易であろう。

さらに、後者が非常に複雑である場合、常に2つのクエリが単一のクエリより遅くなるわけではないため、このアプローチは必ずしもパフォーマンス面で最悪ではありません。

4番目の方法は、完全にカスタムのSQLを作成することです。トリックをするかもしれない(漠然とテストされた)クエリは以下の通りです:

$products_and_variations = $wpdb->get_results("
  SELECT * FROM (

    SELECT products.* FROM {$wpdb->posts} products
      LEFT JOIN {$wpdb->postmeta} meta1 ON meta1.post_id = products.ID
      LEFT JOIN {$wpdb->postmeta} meta2 ON meta2.post_id = products.ID
      WHERE products.post_type = 'product'
      AND products.post_status = 'publish'
      AND (meta1.meta_key = '_visible' AND meta1.meta_value = '1')
      AND (meta2.meta_key = '_available' AND meta2.meta_value = '1')
      GROUP BY products.ID

    UNION

    SELECT variations.* FROM {$wpdb->posts} variations
      LEFT JOIN {$wpdb->postmeta} meta3 ON meta3.post_id = variations.ID
      WHERE variations.post_type = 'product_variation'
      AND variations.post_status = 'publish'
      AND (meta3.meta_key = '_featured' AND meta3.meta_value = '1')
      GROUP BY variations.ID
  )
  posts
    ORDER BY post_date DESC
    LIMIT 0, 100
");

あなたは気づくことができます:

  • それがどれほど複雑か
  • 製品に対して制限を使用し、バリエーションに対して異なる制限を使用することはできません。mergedの結果を制限することのみが可能です。

このようなものを使用する前に、2つのクエリによるアプローチと比較してパフォーマンスをテストし、パフォーマンスが大幅に向上した場合にのみカスタムクエリを使用します。

4
gmazzap

これはpre_get_postsを使った別のアプローチです。

  • 要件に応じて各投稿タイプの all /投稿IDを取得するには、get_posts()(x2クエリ)を使用します。 fieldsパラメータ('fields' => 'ids')をクエリの引数に追加して、ポストIDを取得するだけです。これによりパフォーマンスが大幅に向上し、リソースの面でそれほど難しくありません。だから、あなたは2つのポストIDの配列になってしまうでしょう

  • 2つの配列をarray_merge()とマージして、すべてのポストIDを保持する「スーパー」配列を作成する必要があります。

  • あなたはこれを一時的なものに保存し、投稿が更新されたとき、一時的な状態を消去するとき、一時的な状態をフラッシュするとき、または投稿されていないときに公開します。これはあなたのサイトのパフォーマンスを構成しないことを保証します。

  • あなたのニーズに合ったIDの配列ができたので、post__inのpost_idの配列をpre_get_postsに渡すことができます。

例(、これは単なる例示にすぎないのでテストしていません。残念ながら、適切なコーディングのための時間はありません

function get_all_special_posts()
{
     /*
     * This is here were you would want to introduce your transient
     */
    $args1 = [
        'post_type' => 'post_type_1',
        'fields' => 'ids',
        'nopaging' => true,
        'meta_query' => [[ /*Your specific custom fields */ ]]
    ];
    $query1 = get_posts( $args1 );

    $args2 = [
        'post_type' => 'post_type_2',
        'fields' => 'ids',
        'nopaging' => true,
        'meta_query' => [[ /*Your specific custom fields */ ]]
    ];
    $query2 = get_posts( $args2 );

    $post_ids = array_merge( $query1, $query2 );

    /*
     * Set your transient here, you would want to store $post_ids in the transient
     */

    return $post_ids;
}

ポストパブリッシュ、ゴミ箱、ゴミ捨て、更新時に一時的なものをフラッシュする関数を忘れずに作成してください。これについてはtransition_post_statusを見てください。非常に用途の広いフック2つの希望する投稿タイプのみをターゲットにするには、$post->post_typeを使用してください

add_action( 'pre_get_posts', function ( $q )
{
    if ( !is_admin() && $q->is_main_query() && $q->is_home() ) {
        $q->set( 'post__in', get_all_special_posts() );
    }
});

念のために、私は最初にget_all_special_posts()が実際に有効な配列を持っているかどうか、そしてそれがpost__inに渡される前にそれが空でないかどうかをチェックします

4
Pieter Goosen