我正试图找出如何为自定义帖子类型编写自定义查询,并在分类法中检索最新的帖子,但有一个转折:每个唯一的术语只有一篇帖子。
如果我找到三个帖子,they would all belong to a separate term.
一个术语可能比被查询的术语有更多的最新帖子,但我希望每个帖子都属于一个唯一的术语。
我知道如何检索所有术语的帖子(见下文),但如何修改上述场景的循环?感谢您的任何建议或意见!
<?php
// Retrieve the terms from our custom tax.
$taxonomy = array(
\'webcomic1_storyline\'
);
$args = array(
\'orderby\' => \'ID\',
\'order\' => \'DESC\',
\'hide_empty\' => false,
);
$categories = get_terms($taxonomy,$args);
// Loop through each term below.
foreach ( $categories as $category ) :
?>
<div class="row">
<section id="<?php echo $category->slug; ?>" class="large-12 columns" data-magellan-destination=\'<?php echo $category->slug; ?>\'>
<h3><?php echo $category->name; ?></h3>
<ul class="work-thumb-container large-block-grid-4 small-block-grid-2">
<?php
// Query below retrieves a single latest post for each term.
$args = array(
\'posts_per_page\' => 1,
\'post_type\' => \'webcomic1\',
\'webcomic1_storyline\' => $category->slug,
\'no_found_rows\' => true,
);
$the_query = new WP_Query( $args );
while ( $the_query->have_posts() ) : $the_query->the_post();
?>
<li>
<figure class="work-thumb large-12 columns">
<a class="cover" href="<?php the_permalink(); ?>"></a>
<?php
if ( has_post_thumbnail() ) {
$image_id = get_post_thumbnail_id();
$image_title = esc_attr( get_the_title( $image_id ) );
$image_link = wp_get_attachment_url( $image_id );
echo \'<img class="th" src="\'. $image_link .\'" alt="\'. $image_title .\'">\';
} else {
echo \'<img class="th" src="\'.get_stylesheet_directory_uri(). \'/img/shop.jpg\'. \'" alt="Overboard cover">\';
}
?>
<figcaption>
<?php
$term_list = wp_get_post_terms($post->ID, \'webcomic1_storyline\', array("fields" => "all")); ?>
<h3 class="teaser-title"><a href="<?php the_permalink(); ?>"><?php echo $term_list[0]->name; ?></a></h3>
<time class="storyline-updated teaser-time" datetime="<?php echo get_the_time(\'c\'); ?>">
<a href="<?php the_permalink(); ?>"> <?php echo get_the_time(\'Y\'); ?> </a>
</time>
</figcaption>
</figure>
</li>
<?php endwhile; ?>
</ul>
</section>
</div><!-- .row -->
<?php endforeach; ?>
</section>
最合适的回答,由SO网友:Pieter Goosen 整理而成
我真的试着想出来了,我得出的结论是,真的没有其他天生的方法可以做到这一点。您的方法可能是另一条路线,但目前来看,运行起来相当昂贵。
我得出的结论是:
在一个与帖子类型和分类相关联的查询中获取所有帖子。为了流式处理查询,我们将只查询帖子ID。这将大大提高性能,因为它可以节省高达99.9%的db查询和运行这些查询所需的时间。
我们将循环查看这些帖子ID并使用get_the_terms()
获取与帖子相关的术语。应注意的是get_the_terms()
缓存,所以这也是非常精简的。
然后,我们将循环浏览帖子术语,并将它们存储在一个数组中,然后将它们与相邻帖子的术语进行比较。前三个帖子ID将保存在一个数组中。这将是最近的3篇帖子,每一篇都来自一个独特的类别。为了加速循环,我们将使用break
一旦我们找到了我们想要的
我们还将实现一些系统,以避免在每次加载页面时都运行该系统
好的,让我们来解决这个问题并编写代码。在我们开始之前,请注意几点。
重要提示:
- 该代码完全未经测试。这可能是马车。请确保在本地测试并启用调试
- 至少需要PHP 5.4。
根据需要修改和滥用代码
我们将使用一个函数,稍后可以在适当的位置调用该函数
/**
* Function get_unique_term_recent_posts
*
* Get most recent posts but each post should be from a
* unique term
*
* @see http://wordpress.stackexchange.com/q/206100/31545
* @param (string) $post type Post type to get posts from
* @param (string) $taxonoy Taxonomy to get posts from
* @param (int|string) $number Amount of posts to get
* @return $unique_ids
*
*/
function get_unique_term_recent_posts( $post_type = \'post\', $taxonomy = \'category\', $number = 3 )
{
// Make sure that no empty values are passed, if so, return false
if ( !$post_type
|| !$taxonomy
|| !$number
)
return false;
// Validate and sanitize input values
$post_type = filter_var( $post_type, FILTER_SANITIZE_STRING );
$taxonomy = filter_var( $taxonomy, FILTER_SANITIZE_STRING );
$number = filter_var( $number, FILTER_VALIDATE_INT );
// Make sure that our taxonomy exist to avoid WP_Error objects when querying post terms, return false
if ( !taxonomy_exists( $taxonomy ) )
return false;
// Save everything in a transient to avoid this being run on every page load
if ( false === ( $unique_ids = get_transient( \'posts_list_\' . md5( $taxonomy . $post_type . $number ) ) ) ) {
// Run our query to get the posts
$args = [
\'post_type\' => $post_type,
\'posts_per_page\' => -1,
\'fields\' => \'ids\' // only get post ids, all we need here
];
$q = get_posts( $args );
// Make sure we have posts before we continue, if not, return false
if ( !$q )
return false;
// Lets update the object term cache
update_object_term_cache( $q, $post_type );
// Set up the varaible which will hold the post ID\'s
$unique_ids = [];
// Set up a variable which will hold term ID for comparion
$term_id_array = [];
// Set up a helper variable which will hold temp term ID\'s for comparion
$term_helper_array = [];
// Start a counter to count posts with terms from $taxonomy
$counter = 0;
// Loop through the posts, get the post terms, loop through them and build an array of post ID\'s
foreach ( $q as $post_id ) {
// Break the foreach loop if the amount of ids in $unique_ids == $number
if ( count( $unique_ids ) == $number )
break;
$terms = get_object_term_cache( $post_id, $taxonomy );
// If post does not have terms, skip post and continue
if ( !$terms )
continue;
/**
* If $term_id_array is empty, this means that this is the first post/newest
* post that will have a unique term, lets save the post ID
*/
if ( !$term_id_array
&& $counter == 0
)
$unique_ids[] = $post_id;
// We do have post terms, loop through them
foreach ( $terms as $term_key=>$term ) {
// Add all term ID\'s in array. Skip comparison on first post, just save the terms
if ( $counter == 0 ) {
$term_id_array[] = $term->term_id;
} else {
if ( in_array( $term->term_id, $term_id_array ) ) {
// Reset the $term_helper_array back to an empty array
$term_helper_array = [];
// Break the loop if the condition evaluate to true
break;
}
// Term ID not found, update the $term_helper_array with the current term ID
$term_helper_array[] = $term->term_id;
} //endif $counter == 0
} // endforeach $terms
/**
* If our helper array, $term_helper_array have terms, we should move them to the $term_id_array array
* and then reset the $term_helper_array back to an empty array.
*
* In short, if $term_helper_array have terms, it means that the specific post has unique terms
* compare to the previous post being save in $unique_ids
*/
if ( $term_helper_array ) {
// If no term ID mathes an ID in the $term_id_array, save the post ID
$unique_ids[] = $post_id;
/**
* Merge the $term_id_array and $term_helper_array. This will hold
* only terms of the post that are stored in the $unique_ids array
*/
$term_id_array = array_merge( $term_id_array, $term_helper_array );
// Reset the $term_helper_array back to an empty array
$term_helper_array = [];
}
// Update our counter
$counter++;
} // endforeach $q
set_transient( \'posts_list_\' . md5( $taxonomy . $post_type . $number ), $unique_ids, 30 * DAY_IN_SECONDS );
}
/**
* We can now return $unique_ids which should hold at most 3 post ID\'s,
* each having a unique term
*/
return $unique_ids;
}
此功能应该可以完美地用于分配了多个术语的帖子(通常情况下是这样的)。如果一个帖子a分配了术语a和术语B,而在帖子B分配了术语B和术语C,那么帖子B将不会显示在返回的ID数组中,因为您希望有两个帖子分配了唯一的术语。您现在可以按如下方式使用该功能:
$post_type = \'webcomic1\';
$taxonomy = \'webcomic1_storyline\';
$post_ids = get_unique_term_recent_posts( $post_type, $taxonomy );
if ( $post_ids ) {
$args = [
\'post__in\' => $post_ids,
\'post_type\' => $post_type,
\'posts_per_page\' => 3
];
$q = new WP_Query( $args );
if ( $q->have_posts() ) {
while ( $q->have_posts() ) {
$q->the_post();
// Your template tags and mark up
} // endwhile
wp_reset_postdata();
} // endif have_posts()
} // endif $post_ids
我已将瞬态设置为在30天内过期,您可以根据需要将其设置为。但要了解更多要点控制,您可能希望在发布、删除、更新或取消删除新帖子时删除此瞬态。为此,我们将在transition_post_status
钩火。他的钩子在所有先前命名的条件下开火。您可以执行以下操作
add_action( \'transition_post_status\', function ()
{
global $wpdb;
$wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE (\'_transient%_posts_list_%\')" );
$wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE (\'_transient_timeout%_posts_list_%\')" );
});
编辑该代码现在已经过测试,并按预期运行,修复了几个bug。只是关于函数的性能
在0.27秒内无瞬态->2db查询。
在0.007秒内完成瞬态->2db查询。
正如您所看到的,db调用保持不变,临时调用的唯一原因是为了节省内存,因为函数在没有临时调用的情况下会使用一点内存来运行,而且,使用的PHP函数也会花费大量时间来执行。因此,本质上,瞬态是用来节省时间和内存的,这在非常大的站点上非常有用