web-dev-qa-db-ja.com

WP_QueryでカテゴリIDを除外または含める

私は300以上のカテゴリーを持つワードプレスのセットアップをしています。

今私はカテゴリを選択するためにいくつかの柔軟性を与えることが必要です。その場合、最初にすべてのカテゴリにチェックマークを付けました。カテゴリを除外する必要がある場合は、選択を解除できます。

今私が直面している問題は、カテゴリーの選択に従って正確な結果を出す方法です。

私の最初のアプローチは、以下のようにすべての選択解除カテゴリーを除外することでした。

例:10,11,12カテゴリを除外

$args = array(
    'category__not_in' => array('10','11','12')
);

category 12 & 13の下にチェックマークが付いた投稿があるとしましょう。上記のコードから、category 12の下の投稿は除外されているため、結果としてその投稿は取得されません。 category 13の選択が解除されていないため、理想的には結果に含まれるはずです。

私の解決策として、私は'category__in'オプションをすべての選択されたカテゴリIDとともに使用することができました。しかし、私の心配はリストが非常に長くなることです - たとえそれがプログラム的に来ても、私は300以上のカテゴリーを持っているので私はwp_queryのオーバーヘッドについて確信がありません。

誰もがこの問題を解決する方法をより良いアイデアを持っています。

6

ご存じのとおり、カテゴリは分類法です。 category__inなどの引数を使用すると、WP_Query()に税金クエリが追加されます。だから、あなたの状況はこのようなものになるでしょう:

$args = array(
    'post_type' => 'post',
    'tax_query' => array(
        'relation' => 'AND',
        array(
            'taxonomy' => 'category',
            'field'    => 'term_id',
            'terms'    => array( 12 ),
            'operator' => 'IN',
        ),
        array(
            'taxonomy' => 'category',
            'field'    => 'term_id',
            'terms'    => array( 11, 12, 13 ),
            'operator' => 'NOT IN',
        ),
    ),
);
$query = new WP_Query( $args );

ここではパフォーマンスの問題については考えません。 SQLクエリを使用してデータベースから投稿を直接クエリしたくない場合は、これが唯一の解決策である可能性が高いです(これによりパフォーマンスが少し向上する可能性があります)。

7
Jack Johansson

4つの投稿と4つのカテゴリがあるとしましょう。

+----+--------+
| ID |  Post  |
+----+--------+
|  1 | Test 1 |
|  2 | Test 2 |
|  3 | Test 3 |
|  4 | Test 4 |
+----+--------+

+----+------------+
| ID |  Category  |
+----+------------+
|  1 | Category 1 |
|  2 | Category 2 |
|  3 | Category 3 |
|  4 | Category 4 |
+----+------------+

+--------+------------------------+
|  Post  |        Category        |
+--------+------------------------+
| Test 1 | Category 1, Category 2 |
| Test 2 | Category 2             |
| Test 3 | Category 3             |
| Test 4 | Category 4             |
+--------+------------------------+

私があなたの質問を正しく理解したならば、あなたはTest 1パラメータを使ってcategory__not_in投稿を得たいです。クエリの引数は次のようになります。

$args = array(
    'category__not_in' => array(2, 3, 4)
);

category__not_inの問題は、それがNOT IN SELECT SQLクエリを生成することです。

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
WHERE 1=1
  AND (wp_posts.ID NOT IN
         ( SELECT object_id
          FROM wp_term_relationships
          WHERE term_taxonomy_id IN (2, 3, 4) ))
  AND wp_posts.post_type = 'post'
  AND (wp_posts.post_status = 'publish'
       OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC LIMIT 0, 10

NOT IN SELECTTest 1を含むすべての投稿を除外します。このSQLだけがNOT IN SELECTの代わりにJOINを使用するならば、これはうまくいきます。

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
WHERE 1=1
  AND (wp_term_relationships.term_taxonomy_id NOT IN (2, 3, 4))
  AND wp_posts.post_type = 'post'
  AND (wp_posts.post_status = 'publish'
       OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC LIMIT 0, 10

上記のSQLはTest 1投稿のみを返します。 WP_Queryクラスを使用してこのようなクエリを作成するには、ちょっとしたコツがあります。 category__not_inパラメータを使用する代わりに、それをcategory__inパラメータに置き換えて、SQLを直接目的のものに変更するpost_whereフィルタを追加します。

function wp_286618_get_posts() {

    $query = new WP_Query( array(
        'post_type' => 'post',
        'category__in' => array( 2, 3, 4 ) // Use `category__in` to force JOIN SQL query.
    ) );

    return $query->get_posts();
}

function wp_286618_replace_in_operator($where, $object) {

    $search = 'term_taxonomy_id IN'; // Search IN operator created by `category__in` parameter.
    $replace = 'term_taxonomy_id NOT IN'; // Replace IN operator to NOT IN

    $where = str_replace($search, $replace, $where);

    return $where;
}

add_filter( 'posts_where', 'wp_286618_replace_in_operator', 10, 2 ); // Add filter to replace IN operator

$posts = wp_286618_get_posts(); // Will return only Test 1 post

remove_filter( 'posts_where', 'wp_286618_replace_in_operator', 10, 2 ); // Remove filter to not affect other queries

他のものに対するこの解決策の利点は、他のカテゴリIDを知る必要がないことです。そして、それはあなたのポストループをきれいに保つでしょう。

5
kierzniak

なぜあなたはすべてのカテゴリを事前チェックしているのですか?すべての結果を表示し、同時にすべてのカテゴリのチェックを外したほうが簡単ではないでしょうか。次に、ユーザーがいくつかを選択すると(誰が300のカテゴリを選択するのですか?)、category__inを使用してクエリを実行できます。

2
grazianodev

これを試してください。これは少し汚れていると思いますが、うまくいきます。

    $skills = get_user_meta($curr_id , 'designer_skills');
    $skills = array_map('intval', explode(',' , $skills[0]));
    $args = array(
            'numberposts' => -1,
            'post_type' => 'project',
            'meta_query'    => array(
                'relation'      => 'AND',
                array(
                    'key'       => 'status',
                    'compare'   => '=',
                    'value'     => 'open'
                ),
                array(
                   'key'        => 'payment_status',
                    'compare'   => '=',
                    'value'     => true
               )
            )
        );
        $posts = get_posts( $args );
        if ($posts) {
            $count = 0;
            foreach ($posts as $project) {
                if (in_array(get_the_terms( $project->ID, 'projects')[0] -> term_id , $skills)){
                    //implement your own code here
                    }
            }
    }
1

パフォーマンスの問題が発生した場合は対処したいと思いますが、MySQLの遅いクエリログを有効にしてmicrotimeを使用してこの特定のクエリ/ハイドレーションにかかる時間を追跡するのであれば。あなたの質問に答えるための簡単な解決策はarray_diffを使うことでしょう。例えば:

$notIn = [10, 11, 12];
$in = [11];

$args = array(
    'category__not_in' => array_diff($notIn, $in)
);
1
JSP

確信はありませんが、WP_Queryのデフォルトの動作/オプションを使用してこれを行うことはできないと思います。だから多分回避策はすべての記事を選択した後あなたのためにこのテストをする機能を実装することでしょう。

もちろん、この方法の欠点は、最初にすべての投稿を選択してからそれらをフィルタリングする必要があることですが、それはあなたの問題の解決策になることができます。だから、あなたはこのような何かをすることができます:

<?php 

function test_categorie($cats,$ex_cats) {

    $test = false; //we consider that this post is excluded until we found a category that doesn't belong to the array
    foreach ($cats as $cat){
        if(!in_array(intval($cat->term_id),$ex_cats)) {
            $test = true;
            //We can exit the loop as this post have at least one category not excluded so we can consider it
            break;
        }

    }
    return $test;
}
//Define the excluded categorie here
$exclude_cat = array(11,12,13);

$args = array(
    'posts_per_page'   => -1,
    'post_type'        => 'post',
);

$the_query = new WP_Query($args );
if ( $the_query->have_posts() ) :
     while ( $the_query->have_posts() ) : $the_query->the_post(); 
            //we do our test, if(false) we don't consider the post and we continue to the next
            if(!test_categorie(get_the_category(),$exclude_cat))
                continue;

            /*  
                Add you code here
            */

    endwhile;
    wp_reset_postdata(); 
endif;

?>
1
Temani Afif