使用wp_query是否可以按分类排序?


49

我的问题很简单,我正在使用WP_Query检索一些使用tax_query通过分类法进行过滤的自定义类型帖子。

现在我的问题是我想按分类法排序,但是从文档和网上搜索中找不到解决方案。

WP_Query中的orderby让您可以按一堆字段(甚至是自定义元字段)进行排序,但它似乎不支持分类法。

任何正确方向的指针?

谢谢你们。

Answers:


12

不,不可能按分类法进行排序,因为从某种类型的角度来看,这实际上没有多大意义。

分类法是将事物组合在一起的方法。因此,对职位进行分类的要点实际上是让该分类中的术语在职位之间共享。如果一个分类法中每个术语仅在一个帖子上使用,那么这将使分类法变得毫无意义。而且,如果按应有的方式共享术语,那么按其排序不会产生任何特别有用的信息。

在这种情况下,您应该使用的是post meta。您可以按帖子元命令进行订购,并且每个帖子都具有唯一性。

编辑:也就是说,您可以通过使用过滤器进行自定义SQL查询来按分类进行排序,而不能通过未修改的WP_Query进行分类:http ://scribu.net/wordpress/sortable-taxonomy-columns.html

但是,如果您不得不采取这种方式,那么您的数据设计结构首先是错误的。分类中的“术语”不是实际的“数据”。这些术语本身没有内在的含义,它们只是它们所描述的特定组的标签。如果将它们视为有意义的数据,则存在潜在的设计缺陷。

分类法通过为事物分配术语来对事物进行分组。分组是分类法的重点,术语只是分组中的漂亮面孔。如果您有有意义的元数据可分配给帖子,则应改为使用帖子元。而且您可以订购,因为post meta同时使用键和值来存储信息。使用分类法,您实际上只存储键,键的值是按该术语分组的帖子。

如果您使用正确的方法,从长远来看,事情会变得容易。我并不是说您不能对分类法做一些奇怪的事情,但从长远来看,您只是在错误地使用它来使事情变得更艰难。


您好奥托,谢谢您的回答。我明白你的意思,也许我走错了路。在我的电视节目站点示例中,我具有系列1,系列2,系列3等的分类法。因此,我可以按系列号对所有不同的电视节目进行分组。然后我对情节(情节01,情节02等)有相同的要求。我要显示的是按情节和意向排序的所有情节列表。我将分析然后发布元和自定义字段。谢谢奥托。
yeope

@yeope,您的分类法应该是系列,术语应该是系列1,系列2,依此类推。对于剧集,我假设一个系列包含多个情节,因此它可以使用相同的分类法“系列”,如果它们是等级分类的,则是第1集,第2集等的父词应为“系列x”。然后,您可以按顺序查询整个系列,使剧集按其应有的顺序排列。
克里斯_O

@Chris_O我明白了,您可能在那里有钱!我能看到的唯一问题是必须为每个系列重复术语“第1集”,“第2集”。也不能根据系列不对所有第1集进行分组,但是我认为可能有解决方法。谢谢Chris_O
yeope 2011年

2
实际上,对分类使用分类法没有多大意义,因为分组是毫无价值的。想想看,如果您将“第1集”作为一个术语,那么您会将第1集与其他电视节目中的第1集进行分组。剧集和系列编号更适合作为post_meta,因为它们专用于该特定节目,并且不能作为一个组使用。电视节目的名称在电视节目分类法中很有用,因为这样您就可以将整个节目分组在一起。
奥托

1
Otto在此之后发表了一篇有趣的博客文章:何时(不)使用自定义分类法
1月Fabry

47

这个问题的可接受答案是不可接受的。假定按税款“毫无意义”是不合逻辑的。他给出的答案没有任何意义。

考虑使用菜单项类型。然后,您有一个“ FoodCategories”的自定义税。FoodCategories税具有“早餐”,“午餐”和“晚餐”术语。现在,如果您使用tax_query参数提交查询,那么您现在将获得一个包含所有条款的结果集,但是这些条款是按发布日期排序的。

为了从这些条款中获得正确的顺序,然后通过将帖子分为不同的类别来适当地显示在前端,您必须遍历结果集,然后查询列表中的每个帖子结果集以查找其术语并与当前术语进行比较,过滤成数组并继续进行。然后,您必须再次遍历新数组以进行显示。这没有生产力。

如果WP具有“ post__in”选项,那么如果WP具有“ tax__in” orderby选项会很好,但是由于没有,因此您要么必须执行上述荒谬的过程,要么必须执行上述操作。通过“ posts_orderby”过滤器和“ posts_join”过滤器自己定制查询,以调整orderby方法并将其分别添加到结果集中;或您必须针对与这些字词相关的html部分中要过滤的每个字词进行新查询。

最有效的方法是通过过滤器更改查询字符串。最简单的方法是执行三个单独的查询。WP API应该按税收或任何限制性查询参数处理订购。如果要基于某些条件限制查询,则很有可能许多人需要在相同条件下进行排序。


2
抱歉,您错了。在您的情况下,按分类进行排序也没有任何意义。您想展示什么?首先是所有早餐,然后是所有晚餐,然后是所有午餐?您应该选择所需的内容以及所需的顺序,但是分类法只是一个分组标签。应该排序的“数据”没有意义。如果是的话,那么它就不应成为分类法中的术语,而应将其设为后元。
奥托

15
来吧,当然,在某些情况下,您将需要按分类术语订购帖子。另一个示例是具有分级分类法的电影帖子类型。在电影列表中,可以很容易地想象人们想要通过评级来订购电影列表,因此所有G级,然后PG级等电影都显示在顶部。(在此示例和进餐示例中,可以通过term_id而不是名称对其进行排序。)在很大的灰色区域中,您可能最好通过分类法而不是元数据来进行服务,但是对分类法进行排序也可能会有所帮助-能够。
SeventhSteel 2015年

2
PG和G评级等都是很好的分类法选择,只是它们是有关特定电影的数据。因此,它们是元的。它们是数据,而不是类别。分类数量有限,只能做出有限的选择。如果需要排序依据,则可以将其设为元,或通过分类法特定代码按分类法强制进行排序。BTW,NC17在PG之后。因此,您仍然需要代码来执行该排序。
奥托

我知道我对这条评论迟到了,但是碰到了这一点。在某些情况下,按分类法排序可能很有意义。我们将一个项目的职位列表作为职位类型,然后将职位所在的州和城市分类。我们希望它们容易被弄乱(显示一个州的所有工作或显示一个城市的所有工作),因此分类法是最好的解决方案。同时,在一般的工作搜索中,我们希望首先按标题,州,城市来对其进行排序。
丹尼斯·普扎克

另一个用例:客户有很多文章,每个文章都有一个类别。客户希望有一个列出所有文章的页面,可以按字母,日期或类别对文章进行排序。类别也可以过滤,但是按字母顺序列出所有文章并不是用例的疯狂,您会看到它经常弹出。
威尔逊·比格斯

15

是的,但是确实涉及...

在您的主题中添加到functions.php中:

function orderby_tax_clauses( $clauses, $wp_query ) {
    global $wpdb;
    $taxonomies = get_taxonomies();
    foreach ($taxonomies as $taxonomy) {
        if ( isset( $wp_query->query['orderby'] ) && $taxonomy == $wp_query->query['orderby'] ) {
            $clauses['join'] .=<<<SQL
LEFT OUTER JOIN {$wpdb->term_relationships} ON {$wpdb->posts}.ID={$wpdb->term_relationships}.object_id
LEFT OUTER JOIN {$wpdb->term_taxonomy} USING (term_taxonomy_id)
LEFT OUTER JOIN {$wpdb->terms} USING (term_id)
SQL;
            $clauses['where'] .= " AND (taxonomy = '{$taxonomy}' OR taxonomy IS NULL)";
            $clauses['groupby'] = "object_id";
            $clauses['orderby'] = "GROUP_CONCAT({$wpdb->terms}.name ORDER BY name ASC) ";
            $clauses['orderby'] .= ( 'ASC' == strtoupper( $wp_query->get('order') ) ) ? 'ASC' : 'DESC';
        }
    }
    return $clauses;
}

    add_filter('posts_clauses', 'orderby_tax_clauses', 10, 2 );

这是从一些发现的东西和我自己做的一些东西中得出的。解释非常艰巨,但是最重要的是运行此命令,您可以输入?orderby =(taxonomy query var)&order = ASC(或DESC),她马上就出发!


谢谢Drew,我将尝试运行该SQL,需要进行一些编辑,但它可能会起作用。我现在唯一的问题是,我可能会按照Otto的指示走错了方向。谢谢你,德鲁。编辑-无需编辑,我可以看到需要调整的地方:)谢谢
yeope 2011年

如果您在最近两分钟之内抓住它,它将无法正常工作,请立即抓住它,我已将其修复。它针对两种特定的分类法进行了设置,我改进了代码以使其适用于所有已注册的分类法。
Drew Gourley

再一次感谢你。以防万一我尝试了您的解决方案,并且可以正常工作。另外,如果有人要使用它,则需要更改add_filter('posts_clauses', 'orderby_tax_clauses', 10, 2 );为“ add_filter('posts_clauses', 'todo_tax_clauses', 10, 2 );谢谢” :)
yeope 2011年

是的,这现在已在代码块中修复,我从正在处理的项目中获取了此内容,尽管我在挂钩中进行了更改,却忘记更改函数的名称。
Drew Gourley

1
您知道是否可以通过ID而不是名称来排序分类法吗?我正在尝试通过ID排序分类组来获得相同的结果
Javier Villanueva

9

我在这里玩游戏迟到了,但是有一种更简单的WordPressy方法可以做到这一点。

像往常一样建立您的税收查询。

$tax_query = array();
$tax_query['relation']="OR";
$tax_query[] = array(
    'taxonomy' => 'product_cat',
    'field'    => 'slug',
    'terms'    => $cat_terms,
);
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;

为query_posts或WP_Query设置参数

$args = array(
    'post_type'=>'post',
    'posts_per_page'=>12,
    'paged'=>$paged,
    'tax_query' => $tax_query,
);

在进行query_posts / WP_Query调用之前,请挂接到orderby过滤器并覆盖它

add_filter('posts_orderby', 'edit_posts_orderby');
function edit_posts_orderby($orderby_statement) {
    $orderby_statement = " term_taxonomy_id ASC ";
    return $orderby_statement;
}
query_posts($args);
remove_filter('posts_orderby', 'edit_posts_orderby');

不要忘了之后删除过滤器...

这对b / c有效,tax_query为您创建联接等,您只需要按联接中的字段之一进行排序。


2
关于如何按名称而不是term_taxonomy_id进行排序的任何想法?在orderby_statement中更改term_taxonomy_id会引发错误
tehlivi

这是对任何有兴趣的人的正确答案!
Mayra M

2

好吧,我想展示一下我按类别/分类对自定义帖子类型进行排序的经验。

网络

  1. 在WordPress上运行的旅行社网站
  2. 自定义帖子类型的主要内容称为“ ruta”
  3. 具有此结构的分类法旅行类型>大陆>国家

案子

在存档类别列表页面中,客户希望帖子按以下类别排序

  1. 大陆,按每条航线上的路线数排序。
  2. 国家,按字母顺序排列。

步骤

首先,我从未修改的存档页面查询中捕获了请求,该请求恰好是这样的:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID 
FROM wp_posts 
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
WHERE 1=1 
AND ( wp_term_relationships.term_taxonomy_id IN (5,6,7,8,9,10,11,12,13,15,16,17,18,19,20,21,22,23,25,26,28,29,31,32,33,35,38,95,101,102,193) )
AND wp_posts.post_type IN ('ruta', 'nav_menu_item') 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 45 
AND wp_posts.post_status = 'private') 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC LIMIT 0, 20

其次,我根据数据库在Sequel Pro中编辑了sql代码。我得出这个结论(是的,可能可以改进:我对MySQL的了解并不突出):

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID, tt1.parent AS pare,
    (
    SELECT COUNT(*) 
    FROM  wp_posts
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
    INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id =      wp_term_relationships.term_taxonomy_id )
    INNER JOIN wp_term_taxonomy AS tt2 ON ( tt2.term_taxonomy_id =  tt1.term_taxonomy_id )
    WHERE 1=1  
    AND tt1.parent = pare
    ) AS Total
FROM  wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id =      wp_term_relationships.term_taxonomy_id )
INNER JOIN wp_terms ON ( tt1.term_id = wp_terms.term_id )
WHERE 1=1  
AND ( wp_term_relationships.term_taxonomy_id IN (5,6,7,8,9,10,11,12,13,15,16,17,18,19,20,21,22,23,25,26,28,29,31,32,33,35,38,95,101,102,193) ) 
AND wp_posts.post_type IN ('ruta', 'nav_menu_item') 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 45 
AND wp_posts.post_status = 'private') 
GROUP BY wp_posts.ID 
ORDER BY
total DESC,
wp_terms.name  

第三,我使用三个过滤器将查询挂接到functions.php文件上:posts_fields,posts_join和posts_orderby

functions.php中的代码:

function xc_query_fields( $fields ) {

   $fields = "wp_posts.ID, wp_posts.post_title, wp_terms.name, tt1.parent AS pare,
    (
    SELECT COUNT(*) 
    FROM  wp_posts
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
    INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id = wp_term_relationships.term_taxonomy_id )
    INNER JOIN wp_term_taxonomy AS tt2 ON ( tt2.term_taxonomy_id = tt1.term_taxonomy_id )
    WHERE 1=1  
    AND tt1.parent = pare
    )
    AS Total";
     return $fields;
}


function xc_query_joins( $join ) {
$join .= "INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
   INNER JOIN wp_term_taxonomy AS tt1 ON ( tt1.term_taxonomy_id = wp_term_relationships.term_taxonomy_id )
   INNER JOIN wp_terms ON ( tt1.term_id = wp_terms.term_id )";
 return $join;
}


function xc_query_orderby( $join ) {
    $join = "total DESC, wp_terms.name ";
    return $join;
 }

最后,我根据一些条件从pre_get_post钩子触发了过滤器

function filtra_queries( $query )
{

  if (  is_archive()  && $query->is_main_query() && !is_admin()  ) {

$rutes = array('viajes-privados', 'asia', 'africa', 'oceania', 'america', 'oriente-proximo');

if  ( in_array( $query->get('category_name'), $rutes ) ) 
  {
  add_filter( 'posts_fields', 'xc_query_fields' );
  add_filter( 'posts_join', 'xc_query_joins' );
  add_filter( 'posts_orderby', 'xc_query_orderby' );
}// end if in_array

  }// end if is_archive

}
 add_filter('pre_get_posts', 'filtra_queries');

希望这可以帮助某人


很好的工作,荒谬的是,将大量的代码用于分类法。WP的巨大问题。
serraosays

2

我遇到了一个非常相似的问题:我想通过自定义分类法(问题)订购自定义的后置存档(杂志文章)。我从不在我的网站上进行直接SQL查询-通常,如果您像其他答案一样,则需要重新考虑您的方法。

问题:

1)Wordpress不允许您以任何智能方式排序分类法。

2)Wordpress只是不允许orderby在帖子类型WP_Query上使用分类法(由Otto阐明)。

解决方案:

1)分类分类法目前最好由“ 定制分类法顺序” NE插件完成。它允许您通过所见即所得来订购分类法,wp-admin这不是我会做的,但是我没有发现更好的方法。

设置插件后,您将获得与我在这里所做的类似的操作。记下该选项Auto-sort Queries of this Taxonomy-将其设置为Custom Order as Defined Above; 这可以为您提供所需的订购。屏幕截图:

自定义分类顺序NE显示

2)有了分类的分类法,您现在可以创建贯穿每个术语的一系列WP_Query调用,有效地创建分类法排序的档案。使用get_terms()创建所有的税收条件的数组,然后运行foreach在每个学期。这WP_Query将为每个术语项创建一个,它将返回给定术语的所有帖子,从而有效地创建按分类术语排序的档案。实现此目的的代码:

  // Get your terms and put them into an array
  $issue_terms = get_terms([
    'taxonomy' => 'issues',
    'hide_empty' => false,
  ]);

  // Run foreach over each term to setup query and display for posts
  foreach ($issue_terms as $issue_term) {
    $the_query = new WP_Query( array(
      'post_type' => 'post',
      'tax_query' => array(
        array(
          'taxonomy' => 'issues',
          'field' => 'slug',
          'terms' => array( $issue_term->slug ),
          'operator' => 'IN'
        )
      )
    ) );

    // Run loop over each query
    while($the_query->have_posts()) :
      $the_query->the_post();

      // YOUR TEMPLATE OUTPUT FOR EACH POST

    endwhile;
  }

本网站上的相关阅读: 以自定义帖子类型显示所有帖子,按自定义分类法分组


2

我不确定为什么这里的所有解决方案都过分夸大了。好的,已经有五年了,但是我现在正在运行以下代码,它可以正常工作:

   <?php // Default
    $wheels_args = array(
        'post_type' => 'wheels',
        'posts_per_page' => '96',
        'orderby' => 'taxonomy, name', // Just enter 2 parameters here, seprated by comma
        'order'=>'ASC'
    );
    $loop = new WP_Query($wheels_args);
    ?>

这将首先按照CPT的分类法(按字母顺序)对这些CPT的分类法进行排序,然后在这些分类法组中按字母顺序对其进行分类。


@yeope为什么这是可接受的答案!?感谢上帝,我滚动了
Juan Solano

1

这是我用于此特定问题的解决方案。此解决方案适用于无法使用pre_get_posts过滤器并且查询中存在现有分页的极端情况(例如:WooCommerce):

global $wpdb;

$taxonomies = array('my-tax-1', 'my-tax-2', 'my-tax-3');

$orderby = "'".implode("', '", array_keys($taxonomies))."'";
$id_sql = $GLOBALS['wp_query']->request;

$id_sql = preg_replace('/LIMIT\s+\d+\s?,?\s\d*/', '', $id_sql);
$id_sql = str_replace('SQL_CALC_FOUND_ROWS', '', $id_sql);

$term_sql = "SELECT
  tt.taxonomy AS `taxonomy`,
  t.name AS `term_name`,
  t.slug AS `term_slug`,
  count(*) AS `term_count`
FROM ({$id_sql}) p 
JOIN wp_term_relationships tr
  ON p.ID = tr.object_id
JOIN wp_term_taxonomy tt
  ON tr.term_taxonomy_id = tt.term_taxonomy_id
JOIN wp_terms t
  ON tt.term_id = t.term_id
WHERE tt.taxonomy IN ({$orderby})
GROUP BY t.slug
ORDER BY
  FIELD(tt.taxonomy, {$orderby})"; // Add further specific ordering here

$results = $wpdb->get_results($term_sql, ARRAY_A);

我已经使用它来创建按分类,术语和每期发布次数排序的导航菜单。

如果您只想要帖子,则将查询更改为SELECT p.*GROUP BY p.ID


0

就像查询之前的查询一样,但是如果我们不查询太多帖子,也不会打扰...想法是修改主查询,因此我们甚至不需要去模板并生成新查询,循环...

function grouped_by_taxonomy_main_query( $query ) {

    if ( $query->is_home() && $query->is_main_query() ) { // Run only on the homepage

        $post_ids = array();

        $terms = get_terms('my_custom_taxonomy');

        foreach ( $terms as $term ) {
            $post_ids = array_merge( $post_ids, get_posts( array( 
                'posts_per_page' => 4, // as you wish...
                'post_type' => 'my_custom_post_type', // If needed... Default is posts
                'fields' => 'ids', // we only want the ids to use later in 'post__in'
                'tax_query' => array( array( 'taxonomy' => $term->taxonomy, 'field' => 'term_id', 'terms' => $term->term_id, )))) // getting posts in the current term
            );
        }

        $query->query_vars['post_type'] = 'my_custom_post_type'; // Again, if needed... Default is posts
        $query->query_vars['posts_per_page'] = 16; // If needed...
        $query->query_vars['post__in'] = $post_ids; // Filtering with the post ids we've obtained above
        $query->query_vars['orderby'] = 'post__in'; // Here we keep the order we generated in the terms loop
        $query->query_vars['ignore_sticky_posts'] = 1; // If you dont want your sticky posts to change the order

    }
}

// Hook my above function to the pre_get_posts action
add_action( 'pre_get_posts', 'grouped_by_taxonomy_main_query' );
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.