Cum să excludem sau să includem ID-uri de categorii în WP_Query

22 nov. 2017, 09:06:04
Vizualizări: 17.8K
Voturi: 7

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ă?

0
Toate răspunsurile la întrebare 5
5
12

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).

26 nov. 2017 10:10:53
Comentarii

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?

Temani Afif Temani Afif
26 nov. 2017 20:40:00

da, din nou ajungem la opțiunea category__in

Janith Chinthana Janith Chinthana
28 nov. 2017 15:44:16

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.

Tim Hallman Tim Hallman
29 nov. 2017 07:45:02

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ă.

Tim Hallman Tim Hallman
29 nov. 2017 07:47:30

Care este beneficiul utilizării lui category__in și category__not__in în loc să folosești categorii negative în ( 'cat', '-5' )

Nora McDougall-Collins Nora McDougall-Collins
7 iul. 2023 20:56:29
0

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ă.

29 nov. 2017 23:50:57
1

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.

23 nov. 2017 00:28:57
Comentarii

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.

Janith Chinthana Janith Chinthana
23 nov. 2017 07:15:46
1

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;

?>
26 nov. 2017 10:33:48
Comentarii

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.

CK MacLeod CK MacLeod
29 nov. 2017 09:55:09
0

î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
                    }
            }
    }
29 nov. 2017 13:44:59