web-dev-qa-db-ja.com

メインループ内の特定のカテゴリの最初の5件の投稿を除外

私は この記事を読んで ループ内の1つの特定のカテゴリにオフセットを設定する方法について/。私はこれが1つ以上のカテゴリーで可能かどうか疑問に思いました。

上のリンク先の記事で提案されているコードに関する私の問題は、私がIDでウィジェットオプションに設定できるカテゴリからの5つの投稿を表示するために単一のウィジェットを使用していることです。私は、すでにループした投稿のpost_IDをどのように収集するかについて頭を包むことはできません。

状況:

ブログページでカスタムウィジェットを使用して、2つの指定されたカテゴリにある最新の5つの投稿を表示します。それらをcategoryAとcategoryBと呼びましょう。

コメントで@Pieter Goosenの質問に回答する:はい、ウィジェットの設定でIDを手動で入力することでカテゴリを指定でき、それはDBに保存されます。

これらのウィジェットの下に、ブログページがすべての私の投稿をループしてそれらを表示します。

効果:

ブログページのメインループ(ウィジェットの下にあるもの)は、categoryAとcategoryBの投稿を再び表示します。上記のウィジェットはすでにカテゴリAとBをループ処理しているため、それらは二重に見えます。

質問:

メインループでcategoryAとcategoryBから最初の5つの投稿を除外することは可能ですか? pre_get_postsでもできますか? get_query_varを使う?どうやって?

編集

私自身のアプローチ

最初の試行

私は私のウィジェットコードをこのように修正しました:

global $do_not_duplicate;
while($featured_posts->have_posts()): $featured_posts->the_post(); 
    $do_not_duplicate[] = get_the_ID();

私のウィジェットは私のfunctions.phpファイルに登録されているので、これらの行を追加しました:

global $do_not_duplicate;
$do_not_duplicate = array();
include('widgets/widgets.php');

最後にindex.phpでは、この行をメインループに追加しました。

if(have_posts()) :
    global $do_not_duplicate;
    while(have_posts()): the_post();
        if( in_array( $post->ID, $do_not_duplicate ) ) continue;
        get_template_part('content');
    endwhile;
endif;

注意してください。これは基本的には動作しますが、メインのクエリから投稿を正確に削除するのではなく、スキップします。これは、デフォルトのposts_per_page設定が満たされず、Jetpackの無限スクロールでは機能しないことを意味します。また、グローバルを使用することは成功する方法ではありません。

2回目の試行

@birgireの提案を修正し、少し修正しました。私は提案されたpre_get_postsフィルターをウィジェットによって使用されるカテゴリーのforeachループと組み合わせて使用​​して、それらによって照会された投稿のIDを集めることを考えました。これが私のスニペットです:

add_action('pre_get_posts', 'modified_mainloop', 10);
function modified_mainloop($query) {
    if(!is_admin() && $query->is_home() && $query->is_main_query()) {
        $cats_to_exclude = get_categories('include=3,4,9');
        foreach($cats_to_exclude as $category) {
            if($category->term_id == '9') {
                $num_posts_to_exclude = 3;
            } else {
                $num_posts_to_exclude = 5;
            }
            $posts_to_exclude = get_posts(array(
                'posts_per_page' => $num_posts_to_exclude,
                'category__in'   => array($category->term_id),
                'fields'         => 'ids'
            ));

        }
        $query->set('post__not_in', (array) $posts_to_exclude);

    }
}

ご注意ください:アイデアは問題ありませんが、コードは機能しません。

2
okiedokey

これは、カテゴリー3と5から、カテゴリーごとに5つの投稿を除外することを試みることができるという概念です。ここで、カテゴリー9のいずれかに属する投稿は除外されます。

これが関数です。( CAVEAT: 新しい短い配列構文を使用したのでPHP 5.4+が必要です。必要に応じて変更してください

/**
 * function get_sticky_category_posts()
 *
 * Get an array of post ids according to arguments given.
 *
 * @author Pieter Goosen
 * @see https://wordpress.stackexchange.com/q/187477/31545
 *
 * @param array $args
 */
function get_sticky_category_posts( $args = [] )
{
    /*
     * If the array is empty or not valid, return null
     */
    if ( empty( $args ) || !is_array( $args ) )
        return null;

    $ids = '';

    foreach ( $args as $k=>$v ) {

        /*
         * Check that $v['taxonomy'], $v['included_terms'] and $v['posts_per_page'] are all set. If not, return null
         */ 
        if (    !isset( $v['taxonomy'] ) 
             || !isset( $v['included_terms'] )
             || !isset( $v['posts_per_page'] )
        )
        return null;

        /*
         * Sanitize and validate user input
         */
        $included_terms = filter_var( $v['included_terms'], FILTER_VALIDATE_INT, ['flags'  => FILTER_FORCE_ARRAY] );
        $taxonomy       = filter_var( $v['taxonomy'], FILTER_SANITIZE_STRING );

        /*
         * Create tax_query according to whether $v['excluded_terms'] is set
         */
        if ( !isset( $v['excluded_terms'] ) ) {
            $tax_query = [
                [
                    'taxonomy'         => $taxonomy,
                    'terms'            => $included_terms,
                    'include_children' => false,
                ],
            ];
        } else {
            $tax_query = [
                [
                    'taxonomy'         => $taxonomy,
                    'terms'            => $included_terms,
                    'include_children' => false,
                ],
                [
                    'taxonomy'         => $taxonomy,
                    'terms'            => filter_var( $v['excluded_terms'], FILTER_VALIDATE_INT, ['flags'  => FILTER_FORCE_ARRAY] ),
                    'include_children' => false,
                    'operator'         => 'NOT IN'
                ],
            ];
        } // endif ( !$v['excluded_term'] ) statement

        /*
         * Use get_posts to get an array of post ids to exclude
         */
        $posts_to_exclude = get_posts(
            [
                'posts_per_page' => filter_var( $v['posts_per_page'], FILTER_VALIDATE_INT ),
                'fields'         => 'ids',
                'tax_query'      => $tax_query
            ]
        );
        if ( $posts_to_exclude ) {

            /*
             * Concatenate all ids into a string using implode
             */
            $ids .= implode( ' ', $posts_to_exclude );
            $ids .= ' ';
        } //endif ( $posts_to_exclude )

    } //end foreach

    /*
     * If we don't have any ids, thus empty string, return null
     */
    if ( !$ids )
        return null;

    /*
     * Create a single flat array of post ids. Use rtrim to remove trailing white space
     */
    return explode( ' ', rtrim( $ids ) );
}

彼女はそれがどのように働くかです:

  • 次の形式で多次元配列を関数の引数に渡す必要があります。

    $args = [
        0 => ['posts_per_page' => 5, 'taxonomy' => 'category', 'included_terms' => 3, 'excluded_terms' => 9],
        1 => ['posts_per_page' => 5, 'taxonomy' => 'category', 'included_terms' => 4, 'excluded_terms' => 9],
    ];
    $a = get_sticky_category_posts( $args );
    
  • 4つの許可されたパラメータがあり、に設定する必要がありますposts_per_pagetaxonomy、およびincluded_terms 整数またはterm ids のパラメータ配列)この関数はnullを返します。他の許可されたパラメータ(これは必須ではなく省略することもできます)はexcluded_termsであり、これは整数またはterm idの配列であり、投稿があってはならない用語です。以下)

    • 5は、分類法のcategoryからの分類法の3からのIDを投稿しますが、39の投稿は返されるべきではありません

    • 5は、分類法のcategoryからの分類法の4からのIDを投稿しますが、49の投稿は返されるべきではありません

    • 関数(var_dump( $a ))から返される配列はこのようになります。

      array(10) {
        [0]=>
        string(3) "159"
        [1]=>
        string(3) "149"
        [2]=>
        string(3) "129"
        [3]=>
        string(3) "126"
        [4]=>
        string(3) "119"
        [5]=>
        string(2) "76"
        [6]=>
        string(3) "528"
        [7]=>
        string(3) "394"
        [8]=>
        string(3) "147"
        [9]=>
        string(2) "97"
      }
      

この配列をpre_get_postspost__not_inパラメータに渡して、投稿を関数から除外することができます。このIDの配列を他の場所で使用することもできます。カスタムクエリ、または この回答で使用されているホームページ以外の他のページのスティッキポストなどの目的にそれらを返すこともできます :-)

これはpre_get_postsのユースケースです。これにより、メインのクエリから必要な投稿が削除されます。 ( 関数内の構文およびクロージャの使用のため、PHP 5.4以降が必要です(クロージャは使用可能です[PHP 5.3+

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Not necessary for home page, included it to be used on any other template like category pages
         && $q->is_main_query() // Make sure this is the main query
         && $q->is_home() // Targets the home page only
    ) {
        if ( function_exists( 'get_sticky_category_posts' ) ) {

            $args = [
                0 => ['posts_per_page' => 5, 'taxonomy' => 'category', 'included_terms' => 3, 'excluded_terms' => 9],
                1 => ['posts_per_page' => 5, 'taxonomy' => 'category', 'included_terms' => 4, 'excluded_terms' => 9],
            ];
            $sticky_cat_posts = get_sticky_category_posts( $args );

            if ( !empty( $sticky_cat_posts ) ) {
                $q->set( 'post__not_in', $sticky_cat_posts );
            }   
        }
    }
}); 
3
Pieter Goosen

この種のpre_get_postsフックアプローチを試すことができます(未テスト)。

add_action( 'pre_get_posts', function( $q )
{
    $exclude_cats = [ 12, 34 ]; // <-- Edit these category ids!

    if(    ! is_admin() 
        && $q->is_main_query()
        && ! is_singular()
        && ! is_category( $exclude_cats ) 
    )
    {
        $exclude_post_ids = get_posts( 
            [
                'fields'         => 'ids',
                'posts_per_page' => 5,
                'category__in'   => $exclude_cats,
            ]
        );
        $q->set( 'post__not_in', (array) $exclude_post_ids ); 
    }
} );

あなたのニーズに合わせてこれをさらに調整する必要があるかもしれないところ。

代わりにカテゴリスラッグを使用することもできますが、tax_queryはカテゴリIDの配列のみをサポートするため、適切なcategory__inを設定する必要があります。

3
birgire