根据帖子所属的术语对帖子进行排序

时间:2015-12-21 作者:Anahit DEV

我有一个分类法叫做portfolio_cat 及其类别。因此,现在我需要创建一个滑块,将该类别作为标题及其帖子项。我怎么能做到?我需要什么样的循环,这样我就可以把他们的帖子放在一个滑块类别中?

我不知道如何自定义此循环以适应

<?php
   $query = new WP_Query( array(\'post_type\' => \'portfolio\', \'posts_per_page\' => 7, \'order\' => ASC ) );
while ( $query->have_posts() ) : $query->the_post(); 
?>

3 个回复
最合适的回答,由SO网友:Pieter Goosen 整理而成

我不打算讨论滑块的实现,这太宽泛了。有几个教程可以让您了解如何实现简单的滑块

这里真正的问题是查询本身。按照帖子所属的术语对查询进行排序是一项相当繁重的操作,如果操作不正确(无意冒犯,正如您在回答中所做的那样),可能会非常昂贵。过度使用的方法是获取所有术语,然后循环遍历它们并为每个术语运行查询,这可能会使您陷入一个100 db调用的操作中,而这仅限于一个只有少量术语和帖子的小型站点。非常大的网站可以在db调用上打包内容,这样可以看到你的SEO评级停留在负数,页面加载次数严重亏损。

我做了一个few posts on this specific subject, 因此,我实际上想将其标记为重复,但我觉得这需要一种不同方法的全新外观。

我在过去所做的是在一个操作中完成所有操作(查询所有帖子,然后对结果排序并显示它们)。然后将此完整的查询结果存储在瞬态中。这里的挫折是,我们在瞬态中存储了大量的序列化数据,这也不是瞬态应该做的事情。

我在这里要做的是构建一个动态排序脚本,它将完成所有艰苦的工作,并在瞬间只存储post ID(这将避免存储大量可能很慢的序列化数据)。然后,我们将使用瞬态中排序的帖子ID来获取我们需要的帖子。因为帖子保存在缓存中,所以我们不会很难命中db来获取所需的帖子,我们只需从缓存中查询并返回已经存在的帖子

几点重要注意事项:

  • 虽然我已经测试了代码,但还没有完全测试。在我的安装中,目前阶段没有明显的bug。但是,在将其转移到生产环境之前,您应该在本地测试安装上启用调试来对其进行全面测试

    无论如何,代码至少需要PHP5。4,但您已经在至少PHP5上了。6,甚至更好,PHP7;-)

    有几个orderby 代码不排除的参数。如果需要,您可以扩展它。查看我链接到的帖子搜索。我做了很多不同的事情usort()

    代码使用返回的第一个术语get_the_terms, 因此,由于使用了第一个术语,具有多个术语的帖子可能会排序错误

    排序脚本:我们要做的是,我们将根据需要获取所有帖子,但我们只返回为节省资源所需的帖子对象属性。然后我们将使用usort() 按条款对帖子进行排序。我们将使其成为动态的,而不是创建需要在需要时进行更改的静态内容,这样您就可以通过只更改传递的参数值来重用它

    /**
     * Function to return an ordered list of post id\'s according to the terms they belong to.
     *
     * This function accepts four parameters
     * @param (string) $taxonomy The taxonomy to get posts and terms from
     * @param (array) $args An array of arguments valid in \'WP_Query\'
     * @param (array) $term_args An array of arguments valid with \'get_terms\'
     * @param (array) $sort An array of parameters used to sort posts in \'usort()\'
     * @return $ids
     */
    function get_sorted_query_results( 
        $taxonomy = \'category\', 
        $args = [], 
        $term_args = [], 
        $sort = [] 
    ) {
        /** 
         * To avoid unexpected ordering, we will avoid any odering that is not done by a WP_Post
         * property. If we incounter any ordering outside that, we would simply return false and
         * stop execution
         */
        $avoid_this_ordering = [
            \'none\', 
            \'type\', 
            \'rand\', 
            \'comment_count\', 
            \'meta_value\', 
            \'meta_value_num\', 
            \'post__in\'
        ];
        if ( isset( $args[\'orderby\'] ) ) {
            if ( in_array( $args[\'orderby\'], $avoid_this_ordering ) )
                return null;
        }
    
        // Validate and sanitize the taxonomy name
        if (    \'category\' !== $taxonomy
             && \'post_tag\' !== $taxonomy
        ) {
            $taxonomy = filter_var( $taxonomy, FILTER_SANITIZE_STRING );
            if ( !taxonomy_exists( $taxonomy ) )
               return null;
        }
    
        // Now that we have run important test, set the transient
    
        // Set a transient to store the post ID\'s. Excellent for performance
        $transient_name = \'pobt_\' . md5( $taxonomy . json_encode( array_merge( $args, $term_args, $sort ) ) );
        if ( false === ( $ids = get_transient ( $transient_name ) ) ) {
    
            // Set up a variable to hold an array of post id and id that should not be duplicated       
            // Set our query defaults
            $defaults = [
                \'posts_per_page\' => -1,
                \'order\'          => \'DESC\',
                \'orderby\'        => \'date\'
            ];
            /**
             * If tax_query isn\'t explicitely set, we will set a default tax_query to ensure we only get posts
             * from the specific taxonomy. Avoid adding and using category and tag parameters
             */
            if ( !isset( $args[\'tax_query\'] ) ) {   
                // Add \'fields\'=>\'ids\' to $term_args to get only term ids
                $term_args[\'fields\'] = \'ids\';
                $terms = get_terms( $taxonomy, $term_args );
                if ( $terms ) { // No need to check for WP_Error because we already now the taxonomy exist
                    $defaults[\'tax_query\'] = [
                        [
                            \'taxonomy\' => $taxonomy,
                            \'terms\'    => $terms
                        ]
                    ];
                } else {
                    // To avoid unexpected output, return null
                    return null;
                }
            }
            // Merge the defaults wih the incoming $args
            $query_args = $defaults;
            if ( $args )
                $query_args = wp_parse_args( $args, $defaults );
    
            // Make sure that \'fields\' is always set to \'all\' and cannot be overridden
            $query_args[\'fields\'] = \'all\';
            // Always allow filters to modify get_posts()
            $query_args[\'suppress_filters\'] = false;
    
            /**
             * Create two separate arrays:
             * - one to hold numeric values like dates and ID\'s 
             * one with lettering strings like names and slugs. 
             * 
             * This will ensure very reliable sorting and also we
             * will use this to get only specific post fields
             */
            $orderby_num_array = [
                \'date\'       => \'post_date\', 
                \'modified\'   => \'post_modified\', 
                \'ID\'         => \'ID\', 
                \'menu_order\' => \'menu_order\',
                \'parent\'     => \'post_parent\',
                \'author\'     => \'post_author\'
            ];
            $orderby_letter_array = [
                \'title\'      => \'post_title\',
                \'name\'       => \'post_name\',
            ];
            // Merge the two arrays to use the combine array in our filter_has_var
            $orderby_comb_array = array_merge( $orderby_num_array, $orderby_letter_array );
    
            //Now we will filter get_posts to just return the post fields we need
            add_filter( \'posts_fields\', function ( $fields ) 
                use ( 
                    $query_args, 
                    $orderby_comb_array 
                )
            {
                global $wpdb;
    
                remove_filter( current_filter(), __FUNCTION__ );
    
                // If $query_args[\'orderby\'] is ID, just get ids
                if ( \'ID\' === $query_args[\'orderby\'] ) { 
                    $fields = "$wpdb->posts.ID";
                } else { 
                    $extra_field = $orderby_comb_array[$query_args[\'orderby\']];
                    $fields = "$wpdb->posts.ID, $wpdb->posts.$extra_field";
                }
    
                return $fields;
            });
    
            // Now we can query our desired posts
            $q = get_posts( $query_args );
    
            if ( !$q ) 
                return null;
    
            /**
             * We must now set defaults to sort by. \'order\' will the order in which terms should be sorted
             * \'orderby\' by defualt will be the value used to sort posts by in the query. This will be used
             * when a post has the same term as another post, then posts should be sorted by this value
             */
            $sort_defaults = [
                \'order\'   => \'ASC\', // This will sort terms from a-z
                \'orderby\' => $query_args[\'orderby\'] // Use the default query order
            ];
    
            // Merge the defaults and incoming args
            $sorting_args = $sort_defaults;
            if ( $sort )
                $sorting_args = wp_parse_args( $sort, $sort_defaults );
    
            /**
             * Now we can loop through the posts and sort them with usort()
             *
             * There is a bug in usort causing the following error:
             * usort(): Array was modified by the user comparison function
             * @see https://bugs.php.net/bug.php?id=50688
             * The only workaround is to suppress the error reporting
             * by using the @ sign before usort in versions before PHP7
             *
             * This bug have been fixed in PHP 7, so you can remove @ in you\'re on PHP 7
             */
            @usort( $q, function ( $a, $b ) 
                use ( 
                    $taxonomy, 
                    $sorting_args, 
                    $orderby_num_array, 
                    $orderby_letter_array 
                )
            {   
    
                /**
                 * Get the respective terms from the posts. We will use the first
                 * term\'s name. We can safely dereference the array as we already
                 * made sure that we get posts that has terms from the selected taxonomy
                 */
                $post_terms_a = get_the_terms( $a, $taxonomy )[0]->name;
                $post_terms_b = get_the_terms( $b, $taxonomy )[0]->name;
    
                // First sort by terms
                if ( $post_terms_a !== $post_terms_b ) {
                    if ( \'ASC\' === $sorting_args[\'order\'] ) {
                        return strcasecmp( $post_terms_a, $post_terms_b );
                    } else { 
                        return strcasecmp( $post_terms_b, $post_terms_a );
                    }
                }
    
                /**
                 * If we reached this point, terms are the same, we need to sort the posts by 
                 * $query_args[\'orderby\']
                 */
                if ( in_array( $sorting_args[\'orderby\'], $orderby_num_array ) ) {
                    if ( \'ASC\' === $sorting_args[\'order\'] ) {
                        return $a->$orderby_num_array[$sorting_args[\'orderby\']] < $b->$orderby_num_array[$sorting_args[\'orderby\']];
                    } else { 
                        return $a->$orderby_num_array[$sorting_args[\'orderby\']] > $b->$orderby_num_array[$sorting_args[\'orderby\']];
                    }
                } elseif ( in_array( $sorting_args[\'orderby\'], $orderby_letter_array ) ) { 
                    if ( \'ASC\' === $sorting_args[\'order\'] ) {
                        return strcasecmp( 
                            $a->$orderby_num_array[$sorting_args[\'orderby\']], 
                            $b->$orderby_num_array[$sorting_args[\'orderby\']] 
                        );
                    } else { 
                        return strcasecmp( 
                            $b->$orderby_num_array[$sorting_args[\'orderby\']], 
                            $a->$orderby_num_array[$sorting_args[\'orderby\']] 
                        );
                    }
                }
            });
    
            // Get all the post id\'s from the posts
            $ids = wp_list_pluck( $q, \'ID\' );
    
            set_transient( $transient_name, $ids, 30*DAY_IN_SECONDS );  
        }
        return $ids;
    }
    
    我对代码进行了注释,使其易于理解。关于参数的一些注释

    • $taxonomy. 这是从中获取术语和帖子的分类法

    • $args. 中可接受的任何有效参数的数组WP_Query. 设置了一些默认值,

      $defaults = [
          \'posts_per_page\' => -1,
          \'order\'          => \'DESC\',
          \'orderby\'        => \'date\'
      ];
      
      因此,您可以使用此参数设置任何附加项,如“post\\u type”和tax_query 如果你需要更具体的帖子

    • $term_args. 有效的参数数组get_terms. 这用于从$taxonomy获取更多特定术语。

    • $sort 用于确定中排序顺序的参数数组usort(). 有效参数为orderorderby

    记住,如果不需要设置参数,并且需要设置一个相邻的参数,则传递一个空数组,除了$taxonomy 必须设置为有效的分类

    示例

    $taxonomy = \'my_taxonomy\';
    $args = [
        \'post_type\' => \'my_post_type\'
    ];
    $term_args = [];
    $sort = [
        \'order\' => \'DESC\'
    ];
    $q = get_sorted_query_results( $taxonomy, $args, $term_args, $sort );
    
    在我的安装中,这将在+/-0.002秒内运行3个db查询。

    我们已将所有相关的已排序帖子ID保存在一个每30天过期一次的瞬态中。当我们发布新帖子、更新、删除或取消删除帖子时,我们也需要刷新它。一般来说,您可以使用transition_post_status 钩子以冲洗上述情况下的瞬态

    add_action( \'transition_post_status\', function ()
    {
        global $wpdb;
        $wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE (\'_transient%_pobt_%\')" );
        $wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE (\'_transient_timeout%_pobt_%\')" );
    });
    
    有很多lot of examples on site with specific uses 针对特定的岗位状态更改。您还可以使用post对象,因此可以针对特定的post类型、作者等

    现在您有了一个排序id的数组,现在可以查询完整的帖子了。在这里,我们将使用post__in 参数和组件orderby 值返回已排序的所需帖子

    根据以上示例,我们可以

    if ( $q ) { // Very important check to avoid undesired results if $q is empty
        $query_args = [
            \'post__in\' => $q,
            \'order\'    => \'ASC\',
            \'orderby\'  => \'post__in\',
            // Any other parameters
        ];
        $query = new WP_Query( $query_args );
        // Run your loop, see code below
    }
    
    我们现在需要做的就是将术语名称显示为标题。这很简单,将当前帖子的术语与前一篇帖子的术语进行比较,如果不匹配,则显示术语名称,否则不显示任何内容

    if ( $query->have_posts() ) {
        // Define variable to hold previous post term name
        $term_string = \'\';
        while ( $query->have_posts() ) {
        $query->the_post();
            // Get the post terms. Use the first term\'s name
            $term_name = get_the_terms( get_the_ID(), \'TAXONOMY_NAME_HERE\' )[0]->name;
            // Display the taxonomy name if previous and current post term name don\'t match
            if ( $term_string != $term_name )
                echo \'<h2>\' . $term_name . \'</h2>\'; // Add styling and tags to suite your needs
    
            // Update the $term_string variable
            $term_string = $term_name;
    
            // REST OF YOUR LOOP
    
        }
        wp_reset_postdata();
    }
    
    这是我在安装时运行的完整查询

    $taxonomy = \'category\';
    $args = [
        \'post_type\' => \'post\'
    ];
    $term_args = [];
    $sort = [
        \'order\' => \'DESC\'
    ];
    $q = get_sorted_query_results( $taxonomy, $args, $term_args, $sort );
    
    if ( $q ) { // Very important check to avoid undesired results if $q is empty
        $query_args = [
            \'posts_per_page\' => count( $q ),
            \'post__in\'       => $q,
            \'order\'          => \'ASC\',
            \'orderby\'        => \'post__in\',
            // Any other parameters
        ];
        $query = new WP_Query( $query_args );
    
        if ( $query->have_posts() ) {
            // Define variable to hold previous post term name
            $term_string = \'\';
            while ( $query->have_posts() ) {
            $query->the_post();
                // Get the post terms. Use the first term\'s name
                $term_name = get_the_terms( get_the_ID(), \'category\' )[0]->name;
                // Display the taxonomy name if previous and current post term name don\'t match
                if ( $term_string != $term_name )
                    echo \'<h2>\' . $term_name . \'</h2>\'; // Add styling and tags to suite your needs
    
                // Update the $term_string variable
                $term_string = $term_name;
    
                // REST OF YOUR LOOP
                the_title();
    
            }
            wp_reset_postdata();
        }
    }
    
    在21篇帖子和6个类别的测试中,整个操作在0.1秒内花费了我7次查询

    使用过度使用的easy方法

    $terms= get_terms( \'category\' );
    foreach ( $terms as $term ) {
        echo $term->name;
        $args = [
            \'tax_query\' => [
                [
                    \'taxonomy\' => \'category\',
                    \'terms\' => $term->term_id,
                ]
            ],
        ];
        $query = new WP_Query( $args );
        while ( $query->have_posts() ) {
            $query->the_post();
    
            the_title();
        }
        wp_reset_postdata();
    }
    
    我得到了32 查询时间为0.2秒。这就增加了25个查询(是原来的5倍),而只有6个类别和21个帖子。

SO网友:Pmpr

You may do this:

$args = array(
    \'posts_per_page\' => \'-1\',
    \'post_type\' => \'portfolio\'
    \'tax_query\' => array(
        array(
            \'taxonomy\' => \'portfolio_cat\',
            \'field\' => \'name\',
            \'terms\' => array(\'cat1\', \'cat2\', \'cat3\'))
        )
    )
);
$wp_query = new WP_Query( $args );
SO网友:Anahit DEV

以下是工作示例

<?php
$member_group_terms = get_terms( \'member_group\' );
 ?>
Then, loop through each one, running a new query each time:

<?php
 foreach ( $member_group_terms as $member_group_term ) {
 $member_group_query = new WP_Query( array(
    \'post_type\' => \'member\',
    \'tax_query\' => array(
        array(
            \'taxonomy\' => \'member_group\',
            \'field\' => \'slug\',
            \'terms\' => array( $member_group_term->slug ),
            \'operator\' => \'IN\'
        )
    )
) );
?>
<h2><?php echo $member_group_term->name; ?></h2>
<ul>
<?php
if ( $member_group_query->have_posts() ) : while ( $member_group_query->have_posts() ) : $member_group_query->the_post(); ?>
    <li><?php echo the_title(); ?></li>
<?php endwhile; endif; ?>
</ul>
<?php
// Reset things, for good measure
$member_group_query = null;
wp_reset_postdata();
}
?>