在WP_QUERY中排除或包括类别ID

时间:2017-11-22 作者:Janith Chinthana

我有一个wordpress设置,有300多个类别。

现在,我需要在选择类别时提供一些灵活性。在这种情况下,我最初预先勾选了所有类别,如果有人需要排除一个类别,他们可以取消选择它。

现在我面临的问题是如何根据类别选择给出准确的结果。

我的第一个方法是排除所有取消选择的类别,如下所示,

例:排除10、11、12类

$args = array(
    \'category__not_in\' => array(\'10\',\'11\',\'12\')
);
比如说,我有一个帖子被打了勾category 12 & 13. 根据以上代码,我不会得到该职位,因为它是根据category 12. 但理想情况下,结果应该是category 13 未取消选择。

作为我的解决方案,我可以使用\'category__in\' 具有所有选定类别ID的选项。但我担心的是,即使它是通过编程实现的,列表也会很长,我不确定wp_query 开销,因为我有300多个类别。

任何人都知道如何解决这个问题。

6 个回复
SO网友:Johansson

您可能知道,类别是分类法。当您使用以下参数时category__in, 它将向您的WP_Query(). 所以,你的情况是这样的:

$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 );
我不会想到这里的性能问题。如果您不想使用SQL查询直接从数据库中查询帖子,那么这很可能是您唯一的解决方案(这可能会稍微提高性能)。

SO网友:kierzniak

假设我们有4个帖子和4个类别。

+----+--------+
| 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             |
+--------+------------------------+
如果我正确理解了你的问题,你想Test 1 post使用category__not_in 参数查询的参数如下所示:

$args = array(
    \'category__not_in\' => array(2, 3, 4)
);
问题在于category__not_in 是它产生的NOT IN SELECT SQL查询。

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 将排除所有帖子,包括Test 1. 如果这个SQL使用JOIN 而不是NOT IN SELECT 这将起作用。

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
以上SQL将仅返回Test 1 邮递我们可以使用WP\\u查询类来生成这样的查询。而不是使用category__not_in 参数替换为category__in 参数和添加post_where 将根据我们的目的直接修改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
与其他解决方案相比,此解决方案的优点是我不需要知道其他类别的ID,它将保持您的post循环干净。

SO网友:grazianodev

为什么要预先勾选所有类别?显示所有结果,同时取消所有类别的勾选,不是更容易吗?然后,当用户选择一些时(谁将选择300个类别?)可以使用运行查询category__in.

SO网友:Temani Afif

我不确定,但我认为您无法使用WP_Query. 因此,可能一个解决方法是实现一个功能,在选择所有帖子后为您执行此测试。

当然,这种方法的缺点是,您必须先选择所有帖子,然后过滤它们,但它可以解决您的问题。所以你可以这样做:

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

?>

SO网友:JSP

我想如果出现性能问题,我会解决,但如果您真的担心,请打开MySQL的慢速查询日志并使用microtime 跟踪此特定查询/水合作用需要多长时间。要回答您的问题,一个简单的解决方案是array_diff. 例如:

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

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

SO网友:Alexander Taranenko

试试这个。我想这有点脏,但对我来说很好!

    $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
                    }
            }
    }

结束