web-dev-qa-db-fra.com

Exclure ou inclure les identifiants de catégorie dans WP_Query

J'ai une configuration wordpress qui a plus de 300 catégories.

Maintenant, je dois donner une certaine flexibilité pour choisir les catégories. Dans ce cas, j'ai préalablement coché toutes les catégories, si quelqu'un doit exclure une catégorie, il peut la désélectionner.

Maintenant, le problème auquel je suis confronté est de savoir comment donner des résultats précis en fonction de la sélection des catégories.

Ma première approche consistait simplement à exclure toutes les catégories désélectionnées comme ci-dessous,

ex: exclure 10,11,12 catégories

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

Disons que j'ai un message qui a été coché sous category 12 & 13. Par le code ci-dessus, je ne recevrai pas cette publication car elle exclut les publications sous le category 12. Mais idéalement, il devrait figurer dans les résultats, car category 13 n'a pas été désélectionné.

Comme solution, je pourrais utiliser l’option 'category__in' avec tous les identifiants de catégorie sélectionnés. Mais mon inquiétude est que la liste serait très longue même si elle vient par programmation, je ne suis pas sûre de la surcharge wp_query car j'ai plus de 300 catégories.

Tout le monde a une meilleure idée de la façon de résoudre ce problème.

6
Janith Chinthana

Comme vous le savez probablement, les catégories sont des taxonomies. Lorsque vous utilisez des arguments tels que category__in, une requête fiscale sera ajoutée à votre WP_Query(). Donc, votre situation ressemblerait à ceci:

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

Je ne penserais pas à des problèmes de performance ici. C'est probablement votre seule solution si vous ne souhaitez pas interroger directement les publications de la base de données à l'aide d'une requête SQL (cela pourrait améliorer légèrement les performances).

7
Jack Johansson

Supposons que nous avons 4 articles et 4 catégories.

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

Si j'ai bien compris votre question, vous souhaitez obtenir le message Test 1 à l'aide du paramètre category__not_in. Les arguments de votre requête ressembleront à ceci:

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

Le problème avec category__not_in est qu’il génère une requête 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 exclura tous les articles, y compris Test 1. Si seulement ce SQL utiliserait JOIN au lieu de NOT IN SELECT, cela fonctionnerait.

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

Above SQL ne renverra que Test 1 post. Nous pouvons faire une petite astuce pour produire une telle requête en utilisant la classe WP_Query. Au lieu d'utiliser le paramètre category__not_in, remplacez-le par le paramètre category__in et ajoutez le filtre post_where, qui modifiera directement le code SQL.

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

L'avantage de cette solution par rapport aux autres solutions est que je n'ai pas besoin de connaître l'ID des autres catégories et que votre boucle de publication restera propre.

5
kierzniak

Pourquoi pré-cochez-vous toutes les catégories? N’est-il pas plus facile d’afficher tous les résultats tout en ayant toutes les catégories décochées? Ensuite, lorsque l'utilisateur sélectionne quelques-unes (qui va sélectionner 300 catégories?), Vous pouvez exécuter une requête avec category__in.

2
grazianodev

essaye celui-là. Je suppose que c'est un peu sale, mais ça marche pour moi bien!

    $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

Je pense que je m'attaquerais aux problèmes de performances si et quand ils surgissaient, mais si vous êtes vraiment inquiet, ouvrez le journal de requêtes lent de MySQL et utilisez microtime pour suivre le temps que prend cette requête/hydratation particulière. Pour répondre à votre question, une solution simple consisterait à utiliser array_diff. Par exemple:

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

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

Je ne suis pas sûr mais je pense que vous ne pouvez pas faire cela en utilisant le comportement/les options par défaut de WP_Query. Alors peut-être qu’un moyen de contourner le problème sera d’implémenter une fonction qui effectuera ce test pour vous après avoir sélectionné tous les articles.

Bien sûr, l’inconvénient de cette méthode est que vous devez d’abord sélectionner tous les articles, puis les filtrer, mais cela peut être une solution à votre problème. Donc, vous pouvez faire quelque chose comme ça:

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