Escludere o includere ID di categoria in WP_Query

22 nov 2017, 09:06:04
Visualizzazioni: 17.8K
Voti: 7

Ho una configurazione WordPress con più di 300 categorie.

Ora ho la necessità di offrire una certa flessibilità nella scelta delle categorie. Inizialmente ho preselezionato tutte le categorie, e se qualcuno vuole escluderne una può deselezionarla.

Il problema che sto affrontando è come fornire risultati accurati in base alla selezione delle categorie.

Il mio primo approccio è stato semplicemente escludere tutte le categorie deselezionate come mostrato sotto:

esempio: escludi le categorie 10, 11, 12

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

Supponiamo di avere un post assegnato alle categorie 12 e 13. Con il codice sopra non otterrei quel post come risultato perché esclude i post sotto la categoria 12. Ma idealmente dovrebbe essere nei risultati perché la categoria 13 non era deselezionata.

Come soluzione potrei usare l'opzione 'category__in' con tutti gli ID delle categorie selezionate. Ma la mia preoccupazione è che la lista sarebbe molto lunga anche se generata programmaticamente, e non sono sicuro dell'overhead di wp_query avendo più di 300 categorie.

Qualcuno ha un'idea migliore per risolvere questo problema?

0
Tutte le risposte alla domanda 5
5
12

Come probabilmente saprai, le categorie sono tassonomie. Quando utilizzi argomenti come category__in, verrà aggiunta una query tassonomica al tuo WP_Query(). Quindi, la tua situazione sarebbe qualcosa del genere:

$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 );

Non penserei a problemi di prestazioni in questo caso. Questa è molto probabilmente la tua unica soluzione, se non vuoi interrogare direttamente i post dal database utilizzando una query SQL (questo potrebbe migliorare leggermente le prestazioni).

26 nov 2017 10:10:53
Commenti

Ma non credi che avremo sempre lo stesso problema? L'OP voleva usare qualcosa per evitare di elencare tutte le categorie, ma nel tuo caso sarai obbligato a elencare le categorie all'interno della parte IN, giusto?

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

sì, si riduce ancora all'opzione category__in

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

category__in o category__and è ciò che ti serve qui come dice Jack. Se vuoi velocizzare le tue query, potresti considerare l'uso dei transients.

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

Inoltre, anche la risposta di graziondev qui sotto è corretta, non c'è bisogno di usare category__in quando fai la tua query iniziale visto che stai includendo tutte le categorie di default. Carica quella query nel tuo transient e poi filtra le richieste successive da quei dati se vuoi velocità.

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

Qual è il vantaggio di usare category__in e category__not__in invece di usare semplicemente categorie negative in ( 'cat', '-5' )

Nora McDougall-Collins Nora McDougall-Collins
7 lug 2023 20:56:29
0

Supponiamo di avere 4 post e 4 categorie.

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

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

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

Se ho capito correttamente la tua domanda, vuoi ottenere il post Test 1 utilizzando il parametro category__not_in. Gli argomenti per la tua query saranno simili a:

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

Il problema con category__not_in è che produce una query SQL 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 escluderà tutti i post incluso Test 1. Se solo questa SQL utilizzasse JOIN invece di NOT IN SELECT funzionerebbe.

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

La SQL sopra restituirà solo il post Test 1. Possiamo fare un piccolo trucco per produrre una query del genere utilizzando la classe WP_Query. Invece di usare il parametro category__not_in, sostituiscilo con il parametro category__in e aggiungi un filtro post_where che modificherà direttamente la SQL per il nostro scopo.

function wp_286618_get_posts() {

    $query = new WP_Query( array(
        'post_type' => 'post',
        'category__in' => array( 2, 3, 4 ) // Usa `category__in` per forzare la query SQL JOIN.
    ) );

    return $query->get_posts();
}

function wp_286618_replace_in_operator($where, $object) {

    $search = 'term_taxonomy_id IN'; // Cerca l'operatore IN creato dal parametro `category__in`.
    $replace = 'term_taxonomy_id NOT IN'; // Sostituisci l'operatore IN con NOT IN

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

    return $where;
}

add_filter( 'posts_where', 'wp_286618_replace_in_operator', 10, 2 ); // Aggiungi il filtro per sostituire l'operatore IN

$posts = wp_286618_get_posts(); // Restituirà solo il post Test 1

remove_filter( 'posts_where', 'wp_286618_replace_in_operator', 10, 2 ); // Rimuovi il filtro per non influenzare altre query

Il vantaggio di questa soluzione rispetto ad altre è che non ho bisogno di conoscere gli ID delle altre categorie e manterrà il tuo post loop pulito.

29 nov 2017 23:50:57
1

Perché pre-selezioni tutte le categorie? Non sarebbe più semplice mostrare tutti i risultati e allo stesso tempo avere tutte le categorie deselezionate? Poi, quando l'utente ne seleziona alcune (chi mai selezionerebbe 300 categorie?), puoi eseguire una query con category__in.

23 nov 2017 00:28:57
Commenti

Non è un'opzione per me però. Normalmente le persone tendono a selezionare quasi tutte le categorie, ma alcune di loro non vogliono vedere post non necessari, quindi in quel caso potrebbero deselezionare le categorie. Ma secondo la mia stima, più del 90% delle categorie sarebbe nella selezione. Questa è la ragione per cui stiamo dando la possibilità di deselezionare piuttosto che selezionare.

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

Non sono sicuro ma penso che non si possa fare questo utilizzando il comportamento/opzioni predefinite di WP_Query. Quindi forse una soluzione alternativa sarebbe implementare una funzione che faccia questo test per te dopo aver selezionato tutti i post.

Ovviamente, lo svantaggio di questo metodo è che devi prima selezionare tutti i post e poi filtrarli, ma può essere una soluzione al tuo problema. Quindi puoi fare qualcosa come questo:

<?php 

function test_categorie($cats,$ex_cats) {

    $test = false; //consideriamo che questo post è escluso finché non troviamo una categoria che non appartiene all'array
    foreach ($cats as $cat){
        if(!in_array(intval($cat->term_id),$ex_cats)) {
            $test = true;
            //Possiamo uscire dal ciclo poiché questo post ha almeno una categoria non esclusa, quindi possiamo considerarlo
            break;
        }

    }
    return $test;
}
//Definisci qui le categorie escluse
$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(); 
            //facciamo il nostro test, se(false) non consideriamo il post e passiamo al successivo
            if(!test_categorie(get_the_category(),$exclude_cat))
                continue;

            /*  
                Aggiungi qui il tuo codice
            */

    endwhile;
    wp_reset_postdata(); 
endif;

?>
26 nov 2017 10:33:48
Commenti

Questa risposta funzionerà, credo, anche se potresti incontrare alcuni problemi se l'uso o l'output dipendono dalla correttezza della query in relazione all'esclusione - ad esempio per una paginazione accurata: potresti ottenere alcune pagine che dovrebbero mostrare, diciamo, 5 elementi, ma che invece ne mostrano 2, per esempio. Ci sono soluzioni anche per questo, ovviamente, anche se ci saranno sempre compromessi di un tipo o di un altro

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

Prova questo. Penso sia un po' sporco, ma per me funziona bene!

    $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)){
                    //implementa qui il tuo codice personalizzato
                    }
            }
    }
29 nov 2017 13:44:59