如何根据匹配的分类术语数显示相关帖子

时间:2019-01-24 作者:Edison

我有一个自定义帖子类型“News”,它有多个自定义分类“News category”、“News tags”。每篇文章包含4-8个“新闻标签”和1个“新闻类别”。

对于我当前的帖子,我希望根据添加到我当前帖子中的常见“新闻标签”术语显示相关帖子,同时也匹配“新闻类别”术语。

因此,我希望相关的帖子能够找到相同“新闻类别”的帖子,其中“新闻标签”术语的数量最多,并按降序显示。

如果我要显示4个相关帖子:

第一篇文章可能有5个共同的“新闻标签”术语,第二篇文章可能有3个共同的“新闻标签”,第三篇文章可能有2个共同的“新闻标签”,最后一篇文章可能有1个共同的“新闻标签”

有可能做到这一点吗?我真的一直在努力解决这个问题,所以我会感谢任何帮助。

2 个回复
最合适的回答,由SO网友:Bram 整理而成

让我们将问题分成三部分:从数据库中检索相关帖子,对它们进行排序并显示结果。

检索后可以使用taxonomy parameters 提供于WP_Query.

让我们首先检索当前帖子的类别并找到它们的ID:

$categories = get_the_terms( get_the_ID(), \'news-category\' );

foreach ( $categories as $category ) {
                        $category_ids[] = $category->term_id;
                    }
我们对标签也这样做:

$tags = get_the_terms( get_the_ID(), \'news-tags\' );

foreach ( $tags as $tag) {
                        $tag_ids[] = $tag->term_id;
                    }
然后,我们构建一组查询参数(我们稍后将其馈送到WP_Query-电话):

$related_args = array(
    \'post_type\'      => array(
        \'news\',
    ),
    \'post_status\'    => \'publish\',
    \'posts_per_page\' => -1, // Get all posts
    \'post__not_in\'   => array( get_the_ID() ), // Hide current post in list of related content
    \'tax_query\'      => array(
        \'relation\' => \'AND\', // Make sure to mach both category and term
        array(
            \'taxonomy\' => \'news-category\',
            \'field\'    => \'term_id\',
            \'terms\'    => $category_ids,
        ),
        array(
            \'taxonomy\' => \'news-tags\',
            \'field\'    => \'term_id\',
            \'terms\'    => $tag_ids,
        ),
    ),
);
最后,我们运行查询,该查询为我们提供

$related_all = new WP_Query( $related_args );
请注意,此查询检索与“过滤器”匹配的所有帖子,因为我们稍后将进行排序。如果我们现在只查询4篇帖子,这些可能不是最相关的。

旁注:上述查询相当繁重,因为它(可能)检索了大量帖子。当我为一个客户项目创建了一个相关的帖子部分(也是一个新闻部分)时,我按日期而不是相关性排序。这允许您对从数据库检索的帖子数量设置限制(更改-14 在您的情况下,并添加\'orderby\' => array( \'date\' => \'DESC\' )$related_args-阵列)。如果您想坚持在重叠处排序,我建议您在查询参数中添加一个日期过滤器,或者将结果数量限制为某个有限值,然后从该集合中检索最相关的帖子

按照上一步进行后排序,$related_all 是一个WP_Query-对象我们按如下方式访问实际帖子,并将其存储在$related_all_posts:

$related_all_posts = $related_all->posts;
这为我们提供了一个更容易使用的阵列。然后,我们循环该数组中的所有结果。根据代码中的注释,当循环遍历结果时,我们会找到与(相关)帖子相关的标签,找到它们的ID,并将其与$tag_ids (关于主帖子)找到之前的内容,看看有多少重叠使用array_intersect():

foreach($related_all_posts as $related_post){
    // Find all tags of the related post under consideration
    $related_post_tags = get_the_terms( $related_post->ID, \'news-tags\' );
        foreach ( $related_post_tags as $related_post_tag ) {
            $related_tag_ids[] = $related_post_tag->term_id;    // Save their IDs in a query
        }

    // Find overlap with tags of main post (in $tag_ids) using array_intersect, and save that number in
    // an array that has the ID of the related post as array key.
    $related_posts_commonality[$related_post->ID] = count(array_intersect($related_tag_ids, $tag_ids));
}
然后,我们使用arsort().

arsort($related_posts_commonality);
最后,我们使用array_slice():

$related_posts_commonality = array_slice($related_posts_commonality, 0, 4);
您可以使用array_keys, e、 g.:

$related_posts_IDs = array_keys($related_posts_commonality);
要实际显示帖子,您可以采取两种方法。您可以使用$related_posts_commonality 要循环查看的结果的数组WP_Query (即。,$related_all), 将匹配的帖子(按正确的顺序)保存在一个新的数组或对象中,并再次循环这些帖子以供显示。因为这不需要额外的查询,所以它可能是最有效的查询。然而,这也是一种痛苦。

因此,您也可以简单地使用我们刚刚找到的ID($related_posts_IDs) 运行另一个查询。

$related_sorted = WP_query(array(\'post__in\' => $related_posts_IDs));
然后,你可以使用loop (while ($related_sorted->have_posts()) 等等)使用以下函数浏览并显示结果the_title()the_content().

SO网友:abgregs

由于给定的帖子类型共享相同的分类法,一个常见的场景可能是根据所有分类法中匹配术语的数量(无论它们的数量或名称)来查找相关帖子,因此我为此创建了一个片段。这种方法不需要显式定义要检查的自定义分类法(尽管您可以根据需要进行检查),还可以获取所有匹配术语的总数,以便将相关帖子从最高到最低排序。您可以修改代码段,以便仅指定某些分类法,或者更改最多应返回多少相关帖子的限制。

该代码段有注释可供解释,但基本上在模板中,此函数将接受当前帖子ID并返回相关帖子的帖子ID数组。从那里,您可以检查结果,并且假设至少找到了其他一些具有匹配条件的帖子,您可以循环返回的相关帖子ID,然后使用ID显示关于每个帖子的任何信息(标题、帖子链接等)。

根据OP问题的标题,我想其他人可能会觉得这很有用。

下面是指向该片段的链接。

Get Recent Posts

要在单个帖子模板中使用此功能,您可以执行以下操作:

$current_post_id = get_the_id();
$related_post_ids = get_related_posts($current_post_id);
// if we have related posts...
if ( !empty($related_post_ids) ) {
    foreach ( $related_post_ids as $post_id ) {
        echo \'<h2>\' . get_the_title($post_id) . \'</h2>\';
    }
} else {
    // No related posts found
}
helper函数的代码段:

/**
 * A WordPress helper function that takes the ID of the current post
 * and returns the top three related posts ranked by highest number of taxonomy
 * terms in common with the current post. Alternately, you can modify lines 26-33
 * to exclude certain taxonomies so that we only check for terms in specific taxonomies 
 * to determine the related posts. To include up to the top X related posts instead of
 * up to three, you can modify lines 148-149.
 *
 * In your template to make use of this function you would do something like...
 *
 * $current_post_id = get_the_id();
 * $related_post_ids = get_related_posts($current_post_id);
 * 
 */

function get_related_posts($current_post_id)
    {   
        // Get the post type we\'re dealing with based on the current post ID.
        $post_type = get_post_type($current_post_id);

        // Get all taxonomies of the specified post type of the current post.
        $taxonomies = [];
        $taxonomy_objects = get_object_taxonomies( $post_type, \'objects\' );
        foreach($taxonomy_objects as $taxonomy) {
            // If you want to only check against certain taxonomies, modify this section as needed
            // to set conditions for which taxonomies should be excluded or included. Below is just an example.
            // if ($taxonomy->name !== \'post_format\' && $taxonomy->name !== \'post_tag\') {
            //     array_push($taxonomies, $taxonomy);
            // }
          
            // By default, we will check against all taxonomies.
            array_push($taxonomies, $taxonomy);
        }

        // Get all the posts of the specified post type,
        // excluding the current post, so that we can compare these
        // against the current post.
        $other_posts_args = array(
            \'post_type\'      => $post_type,
            \'post__not_in\'   => array($current_post_id),
        );
        $other_posts = new WP_Query( $other_posts_args );

        wp_reset_postdata();

        // We will create an object for each matching post that will include
        // the ID and count of the number of times it matches any taxonomy term with the current post.
        // Later, when we create those, they will get pushed to this $matching_posts array.
        $matching_posts = array();

        // If we have other posts, loop through them and
        // count matches for any taxonomy terms in common.
        if($other_posts->have_posts()) {

            foreach($taxonomies as $taxonomy) {

                // Get the term IDs of terms for the current post
                // (the post presumably displaying as a single post
                // back in our template, for which were finding related posts).
                $current_post_terms = get_the_terms($current_post_id, $taxonomy->name);


                // Only continue if the current post actually has some terms for this taxonomy.
                if($current_post_terms !== false) {

                    foreach($other_posts->posts as $post) {

                        // Get the term IDs of terms for this taxonomy
                        // for the other post we are currently looping over.
                        $other_post_terms = get_the_terms($post->ID, $taxonomy->name);

                        // Check that other post has terms and only continue if there
                        // are terms to compare.
                        if($other_post_terms !== false) {

                            $other_post_term_IDs = array();
                            $current_post_term_IDs = array();

                            // Get term IDs from each term in the current post.
                            foreach($current_post_terms as $term) {
                                array_push($current_post_term_IDs, $term->term_id);
                            }

                            // Get term IDs from each term in the other post.
                            foreach($other_post_terms as $term) {
                                array_push($other_post_term_IDs, $term->term_id);
                            }

                            if( !empty($other_post_term_IDs) && !empty($current_post_term_IDs) ) {
                                
                                // Collect the matching term IDs for the terms the posts have in common.
                                $match_count = sizeof(array_intersect($other_post_term_IDs, $current_post_term_IDs));
                                
                                // Get the ID of the other post to use to identify and store this post
                                // in our results.
                                $post_ID = $post->ID;

                                if ($match_count > 0) {

                                    // Assume post not added previously.
                                    $post_already_added = false;

                                    // If posts have already been added to our matches
                                    // then check to see if we already added this post.
                                    if(!empty($matching_posts)) {
        
                                        foreach($matching_posts as $post) {
                                            // If this post was added previously then let\'s increment the count
                                            // for our new matching terms.
                                            if (isset($post->ID) && $post->ID == $post_ID) {
                                                $post->count += $match_count;
                                                // Switch this to true for the check we perform below.
                                                $post_already_added = true;
                                            }
                                        }
                                        
                                        // If never found a post with same ID in our $matching_posts
                                        // list then create a new entry associated with this post and add it.
                                        if ($post_already_added === false) {
                                            $new_matching_post = new stdClass();
                                            $new_matching_post->ID = $post_ID;
                                            $new_matching_post->count = $match_count;
                                            array_push($matching_posts, $new_matching_post);
                                        }
                                    } else {
                                        // If no posts have been added yet to $matching_posts then this will be the first.
                                        $new_matching_post = new stdClass();
                                        $new_matching_post->ID = $post_ID;
                                        $new_matching_post->count = $match_count;
                                        array_push($matching_posts, $new_matching_post);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            
            if(!empty($matching_posts)) {
                // Sort the array in order of highest count for total terms in common
                // (most related to least).
                usort($matching_posts, function($a, $b) {
                    return strcmp($b->count, $a->count);
                });

                // Just take the top 3 most related
                $most_related = array_slice($matching_posts, 0, 3);
                
                // Get the IDs of most related posts.
                $matching_posts = array_map(function($obj) {
                    return $obj->ID;
                }, $most_related);
            }
   
        } 

        return $matching_posts;


    }