web-dev-qa-db-ja.com

カスタムクエリ - カテゴリ別の代替投稿

「サービス」というカスタム投稿タイプと、「場所」というカスタム分類法があります。各投稿は「場所A」または「場所B」のいずれかに分類されます。

"services"投稿タイプからすべての投稿をクエリし、日付順に並べ替える必要があります。ただし、単に日付順にリストするのではなく、次のようにカテゴリごとに投稿を変更します。

  1. "Location A"からの最新投稿
  2. "Location B"からの最新投稿
  3. "Location A"から2番目に新しい投稿
  4. "Location B"から2番目に新しい投稿
  5. "Location A"から3番目に新しい投稿
  6. "Location B"から3番目に新しい投稿

等...

これを実現するために次のクエリを変更することは可能ですか?あるいは、私はいくつかの高度なデータベースの結合/マージなどをしなければなりませんか(あなたが想像できるように、私はそれらの質問に精通していません)。

<?php $args = array(
        'post_type' => 'services', 
        'posts_per_page' => '-1',
        'order_by' => 'date',
        'order' => 'DESC',
            'tax_query' => array(
                array(
                    'taxonomy' => 'location',
                    'field' => 'slug',
                    'terms' => array ('location-a', 'location-b')
                )
            )
        );
        $query = new WP_Query( $args );

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

その他の注意事項:

  • 各カテゴリに偶数の投稿があるわけではないので、場所Bからの投稿がなくなったら、場所Aからの投稿を引き続き表示するにはクエリが必要です。
  • 投稿が両方のカテゴリに分類されている場合はどうなりますか?二度見せたくない。
  • カスタム分類法 "location"は、いくつかの異なる投稿タイプで共有されています。ただし、一度に1つの投稿タイプのみをクエリします。
  • 特定の投稿をスティッキーにする機能を保持する必要があります。

これは可能ですか?本当にありがとう。

3
LBF

編集

あなたのコメントから、私はソリューション全体を作り直しました。参考のために元の回答を残しています

3つのスティッキー投稿がある場合、投稿日に関係なく、それらは最初のページの最初の3投稿になるべきではありませんか。何が起こっているのかというと、日付に基づいて最初の10の投稿のセットに含まれているスティッキー投稿だけがスティッキーとして扱われるということです。付箋投稿の残りの部分は、日付に基づいて分類されるページの上部に表示されます。日付に関係なく、最初のページの一番上にそれらを表示するにはどうすればよいですか。

私はあなたが言ったことをテストしました、そしてスティッキーポストの振る舞いは私がWP_Queryの新しい例で税金照会を使用するときそれが予想したほどではありません。

可能な解決策

  • 2つのクエリを実行することができます。1つはスティッキーポスト用、もう1つは私の最初の回答で説明したとおりです。2つめのクエリからスティッキーポストを除外するだけです。元の答えを少し修正する必要があります。これの欠点は、1ページに正確に10の投稿が必要な場合、これは機能しないことです。うまく機能させるには、オフセットを利用する必要があります。

  • 私が行った解決策は、すべての条件を別々に扱い、後でそれらをすべて1つにまとめたものです

_ソリューション_

私はここでいくつかの解決策を試してテストし、最速でリソース集約的でないものを得ました。 OPで設定された要件は非常に独特なので、例えば日付と付箋投稿に従って投稿が必要な場合など、用語に従って投稿を変更する必要がない他の状況では、この解決策はおそらくやり過ぎるでしょう。

完全なソリューションでは最大4つのカスタムクエリを使用しているため、これはdbを使用するのはかなり難しいと思うかもしれませんが、そうではありません。それどころか、これは私の最初の答えよりも実際には2倍速いのです。なぜならあなたは投稿が用語に属しているかどうかを確認するためのチェックをしていないからです。これを大幅にスピードアップするために、私はこの編集を私の元の答えよりもほぼ4倍速くするトランジェントを利用しました。

それでは、コードを書いてみましょう:(最初の答えで扱ったので、特定の点については詳しく説明しません

あなたが見るであろうように私はスティッキーポストのために使われる2つの可能な投稿をすることにしました。あなたはあなたがスティッキをどのように処理したいかを決定し、そしてあなたのニーズに従ってコメントを外してコードをコメントアウトする必要があります。これが私が使った2つの方法です。

  • あなたのスティッキーがすべて2つの設定された用語の範囲内になるだろうならば、あなたは単にそのままコードを使うことができます

  • スティッキーが2つの集合用語の外側からの投稿を含み、2つの集合用語に属さないものを除外する必要がある場合は、コメント解除されたコードをコメント解除してからこの行を削除する必要があります。

    $q = array_merge( $sticky_post, $new_posts_array );
    

コードの基本的な内部動作

特定の用語からの投稿(ここでは、クエリを速くするために投稿IDを取得するだけです)を別々に取得します。その理由は、すべての投稿を一度に取得してから単語でソートするよりも速いことです。重複しないように、2番目のクエリで両方の用語に属するすべての投稿を除外しています。これらの投稿は最初のクエリによってのみ取得されます

次に、返された2つの配列のそれぞれに新しいキーが割り当てられます。1つの配列には偶数キーが割り当てられ、もう1つの配列には不均等キーが割り当てられます。これらの配列は結合されソートされます。新しい配列は両方の配列からの投稿を単語順に交互にソートして保持します

最後のステップはこれにスティッキーポストを追加することです。あなたが今持っているものは、投稿ID、スティッキーな投稿を最初に保持し、それから投稿を単語順に交互にソートして保持する配列です。このIDの配列は一時的に保存され、7日ごとに更新されます(適宜これを設定できます)、または投稿が更新、削除、または新しい投稿が公開されたときに更新されます。

この投稿IDの配列は、通常、投稿をnew WP_Queryで表示するために使用され、投稿IDの並び順は投稿のソートに使用されます。これが完全なコードです。

if ( false === ( $q = get_transient( 'ordered_posts' ) ) ) {

    $sticky_post = get_option( 'sticky_posts' );
    /* 
     * Only do this if sticky posts can belong to terms outside the given two terms
     * and you only need to include sticky posts that belongs to the given two terms
     * Uncomment this part if needed
    */
    /* 
    if( $sticky_post ) {
        $sticky_args = array(
            'post_type'         => 'services', 
            'posts_per_page'    => -1,
            'post__in'          => $sticky_post,
            'fields'            => 'ids',
            'tax_query'         => array(
                array(
                    'taxonomy'  => 'location',
                    'field'     => 'slug',
                    'terms'     => array ('location-a', 'location-b' )
                )
            )
        );
        $sticky_query = get_posts( $sticky_args );
    }
    */

    $args1 = array(
        'post_type'         => 'services', 
        'posts_per_page'    => -1,
        'post__not_in'      => $sticky_post,
        'fields'            => 'ids',
        'tax_query'         => array(
            array(
                'taxonomy'  => 'location',
                'field'     => 'slug',
                'terms'     => array ('location-a')
            )
        )
    );
    $query1 = get_posts( $args1 );

    $new_posts_array1 = [];

    if( $query1 ) {
        $counter1 = 0;

        foreach ( $query1 as $post ) {
            $new_posts_array1[$counter1++ * 2] = $post;
        }
        unset( $post );
    }

    $args2 = array(
        'post_type'         => 'services', 
        'posts_per_page'    => -1,
        'post__not_in'      => $sticky_post,
        'fields'            => 'ids',
        'tax_query'         => array(
                array(
                    'taxonomy'  => 'location',
                    'field'     => 'slug',
                    'terms'     => array ('location-b')
                ),
                array(
                    'taxonomy'  => 'location',
                    'field'     => 'slug',
                    'terms'     => array ('location-a'),
                    'operator'  => 'NOT IN',
                )
            )
    );
    $query2 = get_posts( $args2 );

    $new_posts_array2 = [];

    if( $query2 ) {
        $counter2 = 0;

        foreach ( $query2 as $post ) {
            $new_posts_array2[($counter2++ * 2) + 1] = $post;
        }
        unset( $post );
    }


    $new_posts_array = $new_posts_array1 + $new_posts_array2;
    ksort( $new_posts_array );

    // Comment this line out if you uncommented the other block of code
    $q = array_merge( $sticky_post, $new_posts_array );

    /* 
     * If you going to use the first block of commented-out code
     * then you will need to uncomment this piece of code. Just remember
     * to comment the piece of code above out as stated. You cannot have both
     * pieces of code going at once
    */
    /*
    if( isset( $sticky_query ) ) {
        $q = array_merge( $sticky_query, $new_posts_array );
    }else{
        $q = $new_posts_array;
    }   
    */

    set_transient( 'ordered_posts', $q, 7 * DAY_IN_SECONDS );
}

$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$args = array(
 'post_type' => 'services',
    'paged'                 => $paged,
    'posts_per_page'        => 5,
    'post__in'              => $q,
    'ignore_sticky_posts'   => 1,
    'orderby'               => 'post__in',
);
$query = new WP_Query( $args );

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

        get_template_part( 'content', get_post_format() );

    }

    next_posts_link( 'Older Entries', $query->max_num_pages ); //Remember the $max_pages parameter with custom queries
    previous_posts_link( 'Newer Entries' );

    wp_reset_postdata();
}

それから、functions.phpに以下のコードを追加します。これにより、新しい投稿が公開された場合、投稿が更新された場合、または投稿のステータスが変更された場合に一時的な投稿が削除されます

add_action( 'transition_post_status', function ( $new_status, $old_status, $post )
{
        delete_transient( 'ordered_posts' );

}, 10, 3 );

上記のコードをデバッグする

トランジェントが設定されると、その中のコードを変更することはできません。この変更は、一時的な期限が切れるか削除されると表示されます。コードが機能しない場合は、次の操作を行います。

  • まず、投稿を更新して一時的なものを削除するか、手動でdbから削除します。

  • その後、次の2行を削除してください。

    if ( false === ( $q = get_transient( 'ordered_posts' ) ) ) {
    
  • そして

    set_transient( 'ordered_posts', $q, 7 * DAY_IN_SECONDS );
        }
    
  • これによりトランジェントが設定されなくなります。

  • トランジェントが削除されたら、すべての変数に対してvar_dump( $var )を実行し、目的の出力が得られるかどうかを確認します。特にクエリと返されるものを確認してください

    • 例:クエリ$query1によって返されたものをダンプするには

      var_dump( $query1 );
      

これにより、問題がどこにあるのかがわかります。必要に応じて出力が得られたら、トランジェントのために削除した行を返します。リセットする前にトランジェントが削除されたことを二重に確認するだけで、そうでなければ前の結果が得られます。

もともとの答え

この質問は実際に私がアイデアをいじるのに変化を与えました。この質問には正しい答えが必要だと思います。これであなたのすべての問題が解決することを願っています。

あなたはたった一つの質問でこれをすることができるでしょう。最初にすべきことは、あなたが既に行ったように、一度に2つの用語からあなたの投稿をすべて取得することです。ちょっと注意してください、dateDESCはデフォルトなのでソート順を設定する必要はありません。

カスタム分類法 "location"は、いくつかの異なる投稿タイプで共有されています。ただし、一度に1つの投稿タイプのみをクエリします。

特定の投稿タイプをターゲットにするには、クエリ引数にpost_typeパラメータを追加するだけです。

<?php 
$args = array(
    'post_type' => 'services', 
    'posts_per_page' => '-1',
    'tax_query' => array(
            array(
                'taxonomy' => 'location',
                'field' => 'slug',
                'terms' => array ('location-a', 'location-b')
            )
        )
);
$query = new WP_Query( $args );

if( $query->have_posts() ) {

返された投稿の配列を2つの別々の配列に分割する必要があります。1つはlocation-alocation-b用で、もう1つはスティッキー投稿用です。返されたposts配列は$query->postsに保持されています

投稿が両方のカテゴリに分類されている場合はどうなりますか?二度見せたくない。

location-a または location-bのどちらとして扱うかは、事前に決めておく必要があります。言うことができます、私たちはそれらをlocation-aとして扱います

特定の投稿をスティッキーにする機能を保持する必要があります。

スティッキポストは、返された配列の一番上に表示されます。並べ替え後も、期間に関係なく、常に一番上に配置します。そのため、並べ替えからスティッキー投稿を除外し、後でそのままそのまま返すようにしましょう。

だから、あなたはこのようなことをすることができます

$counter1 = 0;
$counter2 = 0;

$new_posts_array = [];
$sticky = [];
foreach ( $query->posts as $post ) {
    if( is_sticky() ) {
       $sticky[] = $post;
    }elseif( has_term( 'location-a', 'location' ) && has_term( 'location-b', 'location' ) || has_term( 'location-a', 'location' ) ) {
       $new_posts_array[$counter1++ * 2] = $post;
    }else{
       $new_posts_array[($counter2++ * 2) + 1] = $post;
    }
}

すべての問題は今解決されました。 2つの配列があります。1つはスティッキーポストの配列を保持し、もう1つはポストの配列を持つ並べ替えられたキーです。

キーが0から始まる通常のソート順になるように、ポストの配列を自然にソートする必要があります。 (すべてのキーをリセットすることもできますが、これは必須ではありません

ksort($new_posts_array);

2つの配列をマージして、もう一度1つの配列にすることができます。この配列は私たちの投稿を表示するものになります

$q = array_merge( $sticky, $new_posts_array );

あとは、元のpost配列の設定を解除し、元のposts配列を並べ替えた配列でリセットし、ループを巻き戻してリストを出力するだけです。

unset( $query->posts );
$query->posts = $q;

$query->rewind_posts();

while( $query->have_posts() ) { 
   $query->the_post();

  //Display loop elements

}
wp_reset_postdata();

}

今すぐすべての!!!

<?php 
$args = array(
    'post_type' => 'services', 
    'posts_per_page' => '-1',
    'tax_query' => array(
            array(
                'taxonomy' => 'location',
                'field' => 'slug',
                'terms' => array ('location-a', 'location-b')
            )
        )
);
$query = new WP_Query( $args );

if( $query->have_posts() ) {

    $counter1 = 0;
    $counter2 = 0;

    $new_posts_array = [];
    $sticky = [];
    foreach ( $query->posts as $post ) {
        if( is_sticky() ) {
           $sticky[] = $post;
        }elseif( has_term( 'location-a', 'location' ) && has_term( 'location-b', 'location' ) || has_term( 'location-a', 'location' ) ) {
           $new_posts_array[$counter1++ * 2] = $post;
        }else{
           $new_posts_array[($counter2++ * 2) + 1] = $post;
        }
    }

    ksort($new_posts_array);

    $q = array_merge( $sticky, $new_posts_array );

    unset( $query->posts );
    $query->posts = $q;

    $query->rewind_posts();

    while( $query->have_posts() ) { 
       $query->the_post();

      //Display loop elements like
      echo get_the_term_list( $post->ID, 'location');
      the_title(); 

    }
    wp_reset_postdata();

}

ページングに関する質問

// set the "paged" parameter (use 'page' if the query is on a static front page)
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$args = array(
    'post_type'         => 'services', 
    'paged'             => $paged,
    'posts_per_page'    => '5',
    'tax_query'         => array(
            array(
                'taxonomy'  => 'location',
                'field'     => 'slug',
                'terms'     => array ('location-a', 'location-b')
            )
        )
);
$query = new WP_Query( $args );

if( $query->have_posts() ) {

    $counter1 = 0;
    $counter2 = 0;

    $new_posts_array = [];
    $sticky = [];
    foreach ( $query->posts as $post ) {
        if( is_sticky() ) {
           $sticky[] = $post;
        }elseif( has_term( 'location-a', 'location' ) && has_term( 'location-b', 'location' ) || has_term( 'location-a', 'location' ) ) {
           $new_posts_array[$counter1++ * 2] = $post;
        }else{
           $new_posts_array[($counter2++ * 2) + 1] = $post;
        }
    }

    ksort($new_posts_array);

    $q = array_merge( $sticky, $new_posts_array );

    unset( $query->posts );
    $query->posts = $q;

    $query->rewind_posts();

    while( $query->have_posts() ) { 
       $query->the_post();

      //Display loop elements like
      echo get_the_term_list( $post->ID, 'location');
      the_title(); 

    }

    next_posts_link( 'Older Entries', $query->max_num_pages ); //Remember the $max_pages parameter with custom queries
    previous_posts_link( 'Newer Entries' );


    wp_reset_postdata();

}
6
Pieter Goosen