it-swarm.com.de

Kategorie-IDs in WP_Query ausschließen oder einschließen

Ich habe ein WordPress-Setup mit mehr als 300 Kategorien.

Jetzt muss ich einige Flexibilität geben, um die Kategorien zu wählen. In diesem Fall habe ich anfangs alle Kategorien angekreuzt. Wenn jemand eine Kategorie ausschließen muss, kann er sie abwählen.

Das Problem, dem ich jetzt gegenüberstehe, ist, wie man genaue Ergebnisse entsprechend der Kategorieauswahl gibt.

Mein erster Ansatz war, einfach alle Kategorien als "Balg" auszuschließen.

zB: 10,11,12 Kategorien ausschließen

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

Nehmen wir an, ich habe einen Beitrag, der unter category 12 & 13 angekreuzt wurde. Vom obigen Code erhalte ich diesen Beitrag nicht, da er Beiträge unter dem category 12 ausschließt. Aber im Idealfall sollte es in den Ergebnissen sein, da category 13 nicht abgewählt wurde.

Als Lösung könnte ich die Option 'category__in' für alle ausgewählten Kategorie-IDs verwenden. Aber ich mache mir Sorgen, dass die Liste sehr lang sein könnte - obwohl sie programmgesteuert erscheint, bin ich mir nicht sicher, was den wp_query-Aufwand angeht, da ich mehr als 300 Kategorien habe.

Jeder hat eine bessere Idee, wie man dieses Problem löst.

6

Wie Sie wahrscheinlich wissen, sind Kategorien Taxonomien. Wenn Sie Argumente wie category__in verwenden, wird eine Steuerabfrage zu Ihrer WP_Query() hinzugefügt. Ihre Situation wäre also ungefähr so:

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

Ich würde hier nicht an Leistungsprobleme denken. Dies ist höchstwahrscheinlich Ihre einzige Lösung, wenn Sie die Posts nicht direkt mit einer SQL-Abfrage aus der Datenbank abfragen möchten (dies kann die Leistung etwas verbessern).

7
Jack Johansson

Nehmen wir an, wir haben 4 Posts und 4 Kategorien.

+----+--------+
| 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             |
+--------+------------------------+

Wenn ich Ihre Frage richtig verstanden habe, möchten Sie einen Test 1-Beitrag mit dem category__not_in-Parameter erhalten. Die Argumente für Ihre Abfrage sehen folgendermaßen aus:

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

Das Problem mit category__not_in ist, dass es eine NOT IN SELECT SQL-Abfrage erzeugt.

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 schließt alle Beiträge aus, einschließlich Test 1. Wenn nur diese SQL JOIN anstelle von NOT IN SELECT verwenden würde, würde dies funktionieren.

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

Über SQL wird nur Test 1 post zurückgegeben. Wir können einen kleinen Trick machen, um eine solche Abfrage mit der WP_Query-Klasse zu erstellen. Anstatt den category__not_in-Parameter zu verwenden, ersetzen Sie ihn durch den category__in-Parameter und fügen Sie einen post_where-Filter hinzu, der SQL direkt an unseren Zweck anpasst.

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

Der Vorteil dieser Lösung gegenüber anderen ist, dass ich die ID anderer Kategorien nicht kennen muss und Ihre Post-Loop sauber bleibt.

5
kierzniak

Warum kreuzen Sie alle Kategorien an? Ist es nicht einfacher, alle Ergebnisse anzuzeigen und gleichzeitig alle Kategorien zu deaktivieren? Wenn der Benutzer dann einige auswählt (wer wählt 300 Kategorien aus?), Können Sie eine Abfrage mit category__in ausführen.

2
grazianodev

probier diese. Ich denke, das ist ein bisschen schmutzig, aber es funktioniert gut für mich!

    $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

Ich denke, ich würde Leistungsprobleme angehen, wenn sie auftauchen, aber wenn Sie wirklich besorgt sind, schalten Sie das langsame Abfrageprotokoll von MySQL ein und verwenden Sie microtime, um zu verfolgen, wie lange diese bestimmte Abfrage/Hydration dauert. Eine einfache Lösung zur Beantwortung Ihrer Frage wäre die Verwendung von array_diff. Zum Beispiel:

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

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

Ich bin nicht sicher, aber ich denke, Sie können dies nicht mit dem Standardverhalten/den Standardoptionen von WP_Query tun. Vielleicht besteht eine Lösung darin, eine Funktion zu implementieren, die diesen Test für Sie ausführt, nachdem Sie alle Beiträge ausgewählt haben.

Der Nachteil dieser Methode ist natürlich, dass Sie zuerst alle Beiträge auswählen und dann filtern müssen, aber es kann eine Lösung für Ihr Problem sein. Sie können also Folgendes tun:

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