Cum să excludem sau să includem ID-uri de categorii în WP_Query
Am o configurare WordPress care are peste 300 de categorii.
Acum am nevoie să ofer flexibilitate în alegerea categoriilor. În acest caz, am bifat inițial toate categoriile, iar dacă cineva trebuie să excludă o categorie, o poate debifa.
Problema pe care o întâmpin este cum să ofer rezultate precise în funcție de selecția categoriilor.
Prima mea abordare a fost să exclud toate categoriile debifate, așa cum este prezentat mai jos:
exemplu: exclude categoriile 10, 11, 12
$args = array(
'category__not_in' => array('10','11','12')
);
Să presupunem că am un articol care a fost bifat în categoria 12 și 13
. Din codul de mai sus, nu voi obține acel articol ca rezultat, deoarece exclude postările din categoria 12
. Dar, în mod ideal, ar trebui să apară în rezultate, deoarece categoria 13
nu a fost debifată.
Ca soluție, aș putea folosi opțiunea 'category__in'
cu toate ID-urile categoriilor selectate. Dar îmi fac griji că lista ar fi foarte lungă, chiar dacă este generată programatic, și nu sunt sigur despre impactul asupra performanței wp_query
, având în vedere că am peste 300 de categorii.
Are cineva o idee mai bună despre cum să rezolv această problemă?
După cum probabil știi deja, categoriile sunt taxonomii. Când folosești argumente precum category__in
, acesta va adăuga o interogare de taxonomie în WP_Query()
. Deci, situația ta ar arăta cam așa:
$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 );
Nu m-aș gândi la probleme de performanță aici. Aceasta este cel mai probabil singura ta soluție, dacă nu vrei să interoghezi direct postările din baza de date prin intermediul unei interogări SQL (Aceasta ar putea îmbunătăți puțin performanța).

Dar nu crezi că vom avea mereu aceeași problemă? OP dorea să folosească ceva pentru a evita enumerarea tuturor categoriilor, dar în cazul tău va trebui să enumere categoriile în partea IN, nu?

category__in
sau category__and
este ceea ce ai nevoie aici, cum spune Jack. Dacă vrei să accelerezi interogările, poate ar fi bine să consideri utilizarea transientelor.

De asemenea, răspunsul lui graziondev de mai jos este de asemenea corect, nu este nevoie să folosești category__in
atunci când faci prima interogare, deoarece incluzi toate categoriile în mod implicit. Încarcă acea interogare în transient-ul tău și apoi filtrează cererile ulterioare din acele date dacă dorești viteză.

Să presupunem că avem 4 articole și 4 categorii.
+----+--------+
| ID | Articol |
+----+--------+
| 1 | Test 1 |
| 2 | Test 2 |
| 3 | Test 3 |
| 4 | Test 4 |
+----+--------+
+----+------------+
| ID | Categorie |
+----+------------+
| 1 | Categoria 1 |
| 2 | Categoria 2 |
| 3 | Categoria 3 |
| 4 | Categoria 4 |
+----+------------+
+--------+------------------------+
| Articol | Categorie |
+--------+------------------------+
| Test 1 | Categoria 1, Categoria 2 |
| Test 2 | Categoria 2 |
| Test 3 | Categoria 3 |
| Test 4 | Categoria 4 |
+--------+------------------------+
Dacă am înțeles corect întrebarea ta, vrei să obții articolul Test 1
folosind parametrul category__not_in
. Argumentele pentru interogarea ta vor arăta astfel:
$args = array(
'category__not_in' => array(2, 3, 4)
);
Problema cu category__not_in
este că generează o interogare SQL de tip NOT IN SELECT
.
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
va exclude toate articolele, inclusiv Test 1
. Dacă această interogare ar folosi JOIN
în loc de NOT IN SELECT
, ar funcționa.
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
Interogarea de mai sus va returna doar articolul Test 1
. Putem face un mic truc pentru a genera o astfel de interogare folosind clasa WP_Query. În loc să folosim parametrul category__not_in
, îl înlocuim cu parametrul category__in
și adăugăm un filtru post_where
care va modifica direct SQL-ul pentru scopul nostru.
function wp_286618_get_posts() {
$query = new WP_Query( array(
'post_type' => 'post',
'category__in' => array( 2, 3, 4 ) // Folosește `category__in` pentru a forța interogarea SQL JOIN.
) );
return $query->get_posts();
}
function wp_286618_replace_in_operator($where, $object) {
$search = 'term_taxonomy_id IN'; // Caută operatorul IN creat de parametrul `category__in`.
$replace = 'term_taxonomy_id NOT IN'; // Înlocuiește operatorul IN cu NOT IN
$where = str_replace($search, $replace, $where);
return $where;
}
add_filter( 'posts_where', 'wp_286618_replace_in_operator', 10, 2 ); // Adaugă filtru pentru a înlocui operatorul IN
$posts = wp_286618_get_posts(); // Va returna doar articolul Test 1
remove_filter( 'posts_where', 'wp_286618_replace_in_operator', 10, 2 ); // Elimină filtrul pentru a nu afecta alte interogări
Avantajul acestei soluții față de altele este că nu trebuie să cunosc ID-urile altor categorii și va menține bucla ta de articole curată.

De ce bifați în avans toate categoriile? Nu ar fi mai ușor să afișați toate rezultatele și, în același timp, să aveți toate categoriile debifate? Apoi, când utilizatorul selectează câteva (cine va selecta 300 de categorii?), puteți rula o interogare cu category__in
.

nu este o opțiune pentru mine. În mod normal, oamenii tind să selecteze aproape toate categoriile, dar unii dintre ei nu doresc să vadă postări inutile, așa că în acest caz ar putea să deselecteze categoriile. Dar, conform presupunerii mele, mai mult de 90% dintre categorii ar fi în selecție. Acesta este motivul pentru care oferim opțiunea de a deselecta în loc de a selecta.

Nu sunt sigur, dar cred că nu poți face acest lucru folosind comportamentul/opțiunile implicite ale WP_Query
. Așadar, o soluție ar putea fi implementarea unei funcții care să facă acest test pentru tine după selectarea tuturor articolelor.
Desigur, dezavantajul acestei metode este că trebuie mai întâi să selectezi toate articolele și apoi să le filtrezi, dar poate fi o soluție pentru problema ta. Deci poți face ceva de genul acesta:
<?php
function test_categorie($cats,$ex_cats) {
$test = false; //considerăm că acest articol este exclus până când găsim o categorie care nu aparține array-ului
foreach ($cats as $cat){
if(!in_array(intval($cat->term_id),$ex_cats)) {
$test = true;
//Putem ieși din buclă deoarece acest articol are cel puțin o categorie neexclusă, așadar îl putem considera
break;
}
}
return $test;
}
//Definește categoriile excluse aici
$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();
//facem testul nostru, dacă (false) nu luăm în considerare articolul și continuăm cu următorul
if(!test_categorie(get_the_category(),$exclude_cat))
continue;
/*
Adaugă codul tău aici
*/
endwhile;
wp_reset_postdata();
endif;
?>

Acest răspuns va funcționa, cred, deși s-ar putea să întâmpinați unele probleme dacă utilizarea sau rezultatul depinde de corectitudinea interogării în relație cu excluderea - de exemplu pentru paginarea precisă: S-ar putea ca unele pagini care ar trebui să afișeze, să zicem, 5 elemente, să afișeze doar 2, de exemplu. Există soluții și pentru aceasta, desigur, deși întotdeauna vor exista compromisuri de un fel sau altul.

încearcă această variantă. Presupun că este un pic murdară, dar funcționează bine pentru mine!
$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)){
//implementează aici propriul cod
}
}
}
