私は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
のオーバーヘッドについて確信がありません。
誰もがこの問題を解決する方法をより良いアイデアを持っています。
ご存じのとおり、カテゴリは分類法です。 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クエリを使用してデータベースから投稿を直接クエリしたくない場合は、これが唯一の解決策である可能性が高いです(これによりパフォーマンスが少し向上する可能性があります)。
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 SELECT
はTest 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を知る必要がないことです。そして、それはあなたのポストループをきれいに保つでしょう。
なぜあなたはすべてのカテゴリを事前チェックしているのですか?すべての結果を表示し、同時にすべてのカテゴリのチェックを外したほうが簡単ではないでしょうか。次に、ユーザーがいくつかを選択すると(誰が300のカテゴリを選択するのですか?)、category__in
を使用してクエリを実行できます。
これを試してください。これは少し汚れていると思いますが、うまくいきます。
$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
}
}
}
パフォーマンスの問題が発生した場合は対処したいと思いますが、MySQLの遅いクエリログを有効にしてmicrotime
を使用してこの特定のクエリ/ハイドレーションにかかる時間を追跡するのであれば。あなたの質問に答えるための簡単な解決策はarray_diff
を使うことでしょう。例えば:
$notIn = [10, 11, 12];
$in = [11];
$args = array(
'category__not_in' => array_diff($notIn, $in)
);
確信はありませんが、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;
?>