web-dev-qa-db-ja.com

WP_Queryを使用して2つのカテゴリから投稿を取得する方法

私は自分のホームページに「最新の投稿」と「人気の投稿」のセクションを作成するためにWP_Queryを使っています。私はちょうど2つのカテゴリ(9と11)から5つの投稿を引き出そうとしていますが、それは猫9からの投稿のみを表示します。

これが私が「最新の投稿」に使っているphpです -

<ul>
        <?php 

        $cat = array(9,11);
        $showposts = 5;
        $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
        $args=array(
            'category__in' => $cat, 
            'showposts' => $showposts,
            'paged' => $paged,
            'orderby' => 'post_date',
            'order' => 'DESC',
            'post_status' => 'publish',
           );

        $the_query = new WP_Query ( $args ); //the query


        $i = 0;while ($the_query->have_posts() ) : $the_query->the_post(); //start the loop
        ?>

        <?php
        if($i==0){ //Sets the output for the top post
        ?>  
            <li class="first-news">
                <div class="post-thumbnail"><a href="<?php the_permalink(); ?>"><?php the_post_thumbnail(350,187); ?></a></div>
                <h2 class="post-box-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
                <p class="post-meta"><span class="tie-date"><?php the_time('F jS, Y') ?></span></p>
                <div class="entry"><?php the_excerpt(); ?></div>
                <div><a class="more-link" href="<?php the_permalink(); ?>">Read More »</a></div>

            </li>


        <?php
            $i++;
             } else { ?>

                <li class="other-news rar">
                <div class="post-thumbnail"><a href="<?php the_permalink(); ?>"><?php the_post_thumbnail(145,93); ?></a></div>
                <h3 class="post-box-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
                <p class="post-meta"><span class="tie-date"><?php the_time('F jS, Y') ?></span></p>
           </li>

        <?php } endwhile; //end of the loop ?>
        <?php wp_reset_postdata(); // reset the query ?>
        </ul> 

助言がありますか?

5
user3205234

'posts_*'/'posts_request'と組み合わせて'posts_where'フィルタ(str_replacepreg_replace...)を使用した経験では、信頼性が低く、柔軟性がありません。

他のフィルタ変更がそのフィルタの1つを使用すると、予想外の結果が得られ、最悪の場合はSQLエラーが発生するため、信頼性が低いため信頼できません。

議論を変えるので柔軟性がない、例えばカテゴリの場合は 'include_children'、それ以外の場合はコードを再利用します。 2の代わりに3の用語は多くの作業を必要とします。

さらに、コードを互換性があるように調整するには、SQLを手動で編集する必要があります。

そのため、パフォーマンスに関して最善の解決策ではないにしても、ときには、より標準的な _アプローチが最善かつより柔軟なアプローチになることがあります。

そして、パフォーマンスはいくつかのキャッシングトリックで向上させることができます...

私の提案:

  1. usort を使用して、異なるクエリからの投稿を順序付けるための関数を作成します(たとえば、用語ごとに1つ)。
  2. 最初の実行時に別々のクエリを実行し、結果をマージし、それらを順序付けし、それらをキャッシュに入れ、それらを返すような関数を書きます。後続のリクエストではキャッシュされた結果を単に返す
  3. 必要に応じてキャッシュの無効化を処理する

コード

まず投稿を注文する関数:

function my_date_terms_posts_sort( Array $posts, $order = 'DESC' ) {
  if ( ! empty( $posts ) ) {
    usort( $posts, function( WP_Post $a, WP_Post $b ) use ( $order ) {
      $at = (int) mysql2date( 'U', $a->post_date );
      $bt = (int) mysql2date( 'U', $b->post_date );
      $orders = strtoupper($order) === 'ASC' ? array( 1, -1 ) : array( -1, 1 );
      return $at === $bt ? 0 : ( $at > $bt ) ? $orders[0] : $orders[1];
    } );
  }
  return $posts;
}

そして、キャッシュされない関数は次のようになります。

function my_fresh_terms_get_posts( $args, $terms, $tax_query_args = NULL ) {
  $posts = array();
  // we need to know at least the taxonomy
  if ( ! is_array( $tax_query_args ) || ! isset( $tax_query_args['taxonomy'] ) ) return;
  // handle base tax_query
  $base_tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
  // run a query for each term
  foreach ( $terms as $term ) {
    $term_tax_query = wp_parse_args( array(
      'terms' => array( $term ),
      'field' => is_numeric( $term ) ? 'term_id' : 'slug'
    ), $tax_query_args );
    $args['tax_query'] = array_merge( $base_tax_query, array($term_tax_query) );
    $q = new WP_Query( $args ); 
    if ( $q->have_posts() ) {
      // merging retrieved posts in $posts array
      // preventing duplicates using ID as array keys
      $ids = wp_list_pluck( $q->posts, 'ID' );
      $keyed = array_combine( $ids, array_values( $q->posts ) );
      $posts += $keyed;
    }
  }
  return $posts;
}

キャッシュをチェックし、利用可能であればそれを返すか、キャッシュされていない結果を返すようになりました。

function my_terms_get_posts( $args, $terms, $tax_query_args = NULL, $order = 'DESC' ) {
  // we need to know at least the taxonomy
  if ( ! is_array( $tax_query_args ) || ! isset( $tax_query_args['taxonomy'] ) ) return;
  $tax = $tax_query_args['taxonomy'];
  // get cached  results
  $cached = get_transient( "my_terms_get_posts_{$tax}" );
  if ( ! empty( $cached ) ) return $cached;
  // no cached  results, get 'fresh' posts
  $posts = my_fresh_terms_get_posts( $args, $terms, $tax_query_args );
  if ( ! empty($posts) ) {
    // order posts and cache them
    $posts = my_date_terms_posts_sort( $posts, $order );
    set_transient( "my_terms_get_posts_{$tax}",  $posts, DAY_IN_SECONDS );
  }
  return $posts;
}

キャッシュは毎日自動クリーンアップされます ただし、特定の分類法の新しい投稿が追加または更新されるたびにキャッシュを無効にすることができます。それは'set_object_terms'にクリーンキャッシュ機能を追加することで実行できます

add_action( 'set_object_terms', function( $object_id, $terms, $tt_ids, $taxonomy ) {
  $taxonomies = get_taxonomies( array( 'object_type' => array('post') ), 'names' );
  if ( in_array( $taxonomy, (array) $taxonomies ) ) {
    delete_transient( "my_terms_get_posts_{$taxonomy}" );
  }
}, 10, 4 );

使用法

// the number of post to retrieve for each term
// total of posts retrieved will be equat to this number x the number of terms passed
// to my_terms_get_posts function
$perterm = 5;

// first define general args:
$paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;
$args = array(
  'posts_per_page' => $perterm,
  'paged' => $paged,
);

// taxonomy args
$base_tax_args = array(
  'taxonomy' => 'category'
);
$terms = array( 9, 11 ); // is also possible to use slugs

// get posts
$posts = my_terms_get_posts( $args, $terms, $base_tax_args );

// loop
if ( ! empty( $posts ) ) {
  foreach ( $posts as $_post ) {
    global $post;
    setup_postdata( $_post );
    //-------------------------> loop code goes here    
  }
  wp_reset_postdata();
}

関数は、追加の分類法に対するクエリでさえも複雑なクエリを使用するのに十分なほど柔軟です。

例えば。

$args = array(
  'posts_per_page' => $perterm,
  'paged' => $paged,
  'post_status' => 'publish',
  'tax_query' => array(
     'relation' => 'AND',
     array(
       'taxonomy' => 'another_taxonomy',
       'terms' => array( 'foo', 'bar' ),
       'field' => 'id'
     )
   )
);

$base_tax_args = array(
  'taxonomy' => 'category',
  'include_children' => FALSE
);

$terms = array( 'a-category', 'another-one' );

$posts = my_terms_get_posts( $args, $terms, $base_tax_args );

このようにして、$args配列内の各用語について、$base_tax_args配列内に設定された 'tax_query'が、$terms内のtax query引数と動的にマージされます。

昇順のオーダーポストも可能です:

$posts = my_terms_get_posts( $args, $terms, $base_tax_args, 'ASC' );

ご注意ください:

  1. 重要:functionに渡される投稿の中でいくつかの投稿が複数のカテゴリに属する​​場合(たとえばOPの場合、いくつかの投稿がカテゴリ9と11の両方を持つ場合)、関数はその投稿を1回返すため、検索される投稿数は予想される数になりません
  2. コードにはPHP 5.3以降が必要
5
gmazzap

コーデックスによれば

いくつかのカテゴリからの投稿を表示 (カテゴリIDを使用してこれらのカテゴリを持つ投稿を表示):

$query = new WP_Query( 'cat=9,11' );

そして、コーデックスのページ付けパラメータによると、'showposts'は非推奨で'posts_per_page'に置き換えられています。

posts_per_pageint) - 1ページに表示する投稿数( バージョン2.1 で使用可能、showpostsパラメータに置き換え).

4
Mayeenul Islam

あなたが求めているのは、この質問のほぼ複製です: posts_where/posts_joinを使用して独自のネストされたmeta_queryを作成するにはどうすればよいですか?

問題は、@ fischiが示唆しているように、結果がいずれかのカテゴリからのものであり、両方が等しく表される前に投稿制限に達することです。これを機能させるには、UNIONが必要です。 WP_Queryはそのロジックに対応していませんが、フックを使用して機能させることができます。

$cat = 9; // your first category
$showposts = 5; // actual results are twice this value
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$args=array(
  'cat' => $cat, 
  'posts_per_page' => $showposts,
  'paged' => $paged,
  'orderby' => 'post_date',
  'order' => 'DESC',
  'post_status' => 'publish',
);

function create_cat_union($clauses) {
  remove_filter('posts_request','create_cat_union',1);
  $clauses = str_replace('SQL_CALC_FOUND_ROWS','',$clauses,$scfr);

  $scfr = (0 < $scfr) ? 'SQL_CALC_FOUND_ROWS' : '';

  $pattern = 'wp_term_relationships.term_taxonomy_id IN \(([0-9,]+)\)';
  $clause2 = preg_replace('|'.$pattern.'|','wp_term_relationships.term_taxonomy_id IN (11)',$clauses); // manipulate this

  return "SELECT {$scfr} u.* FROM (({$clauses}) UNION ({$clause2})) as u";
}
add_filter('posts_request','create_cat_union',1,2);

$the_query = new WP_Query ( $args ); //the query
var_dump($the_query->posts);

いくつかのメモ:WP_Queryはカテゴリ引数を解析し、最初のUNIONがカテゴリ9のすべての子を含むようにカテゴリの子を検索します。Idid notカテゴリ11のロジックを複製します。その機能が必要な場合は、 WP_Query source を参照できます。 =効果を再現します。

0
s_ha_dum