虽然我已经测试了代码,但还没有完全测试。在我的安装中,目前阶段没有明显的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
必须设置为有效的分类示例
$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个帖子。