4

I need help in getting related posts using multiple tags.

I have posts with the tags poetry, motivational, attitude, rules, lines, sigma and inspirations.

I want related posts matching most of these tags using WP_Query.

Note: each post doesn't have all of these tags.

4

1 Answer 1

5

There is no direct way of getting posts matching most of these tags using WP_Query.

The usual documented methods:

If you want posts matching any of these tags, you may use:

$args = array(
    'post_type' => 'post',
    'tag_slug__in' => array( 'poetry', 'motivational', 'attitude', 'rules', 'lines', 'sigma', 'inspirations' )
);
$query = new WP_Query( $args );

Also, if you want posts matching all of these tags, you may use:

$args = array(
    'post_type' => 'post',
    'tag_slug__and' => array( 'poetry', 'motivational', 'attitude', 'rules', 'lines', 'sigma', 'inspirations' )
);
$query = new WP_Query( $args );

Getting posts with most of these tags:

To achieve what you want, you'll have to either use a custom SQL query or some advanced customizations to the WP_Query generated SQL query using hooks.

For example, you may use the posts_clauses hook combined with pre_get_posts hook to alter the SQL generated by WP_Query.

To get posts with most of these tags, you'll need to include count( wp_term_relationships.object_id) in SELECT portion of the generated SQL query, and then sort the query result by that count in descending order.

The following is the code of a complete plugin that implements this logic:

<?php
/**
 * Plugin Name: @fayaz.dev Related posts by most tags
 * Description: Show related posts by of the provided tags
 * Author: Fayaz Ahmed
 * Version: 1.0.0
 * Author URI: https://fayaz.dev/
 **/

namespace Fayaz\dev;

/**
 * Any WP_Query where 'order_by_most_tags' is set as an argument,
 * will be altered by this function, so that the query result is 
 * ordered by the number of matching tags in descending order.
 * However, if no matching tag is provided, then that query will not
 * be altered by this.
 */
function filter_posts_by_most_tags( $query ) {
    if( isset( $query->query['order_by_most_tags'] )
        && isset( $query->query_vars['tag_slug__in'] ) && ! empty( $query->query_vars['tag_slug__in'] ) ) {
        add_filter( 'posts_clauses', '\Fayaz\dev\add_select_count_tags', 10, 2 );
    }
}
add_action( 'pre_get_posts', '\Fayaz\dev\filter_posts_by_most_tags' );

function add_select_count_tags( $clauses, $wp_query ) {
    global $wpdb;

    // database query modification needed for ordering based on most tags
    $clauses['fields'] = $clauses['fields'] . ', count( ' . $wpdb->term_relationships . '.object_id ) as number_of_tags';
    $clauses['orderby'] = 'number_of_tags DESC, ' . $clauses['orderby'];

    // we only need this once, so it's better to remove the filter after use
    remove_filter( 'posts_clauses', '\Fayaz\dev\add_select_count_tags', 10, 2 );
    return $clauses;
}

Sample Plugin Usage:

The above plugin implements a custom WP_Query argument named order_by_most_tags. When it's set, the plugin will alter the result to get posts with most of the provided tags.

One you activate the plugin, use the code as shown below in your currently active theme's template files (e.g. single.php):

$args = array(
    'post_type' => 'post',
    'tag_slug__in' => array( 'poetry', 'motivational', 'attitude', 'rules', 'lines', 'sigma', 'inspirations' ),
    'order_by_most_tags' => 1,
    // other order by (in case there are multiple matches with most tags)
    'orderby' => array( 'comment_count' => 'DESC', 'date' => 'ASC' )
);
$post_ID = get_the_ID();
if( $post_ID ) {
    // exclude current post
    $args['post__not_in'] = array( $post_ID );
}
echo '<h2>Related Posts</h2>';
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
    echo '<ul>';
    while ( $query->have_posts() ) {
        $query->the_post();
        echo '<li>';
        ?>
            <a href="<?php the_permalink(); ?>" title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a>
        <?php
        echo '</li>';
    }
    echo '</ul';
}
else {
    echo '<h2>Nothing is found</h2>';
}
// Restore original Post Data, needed if this was run in a loop
wp_reset_postdata();
7
  • This is a what I want but I need a little bit more help?
    – Ghost
    Feb 11, 2023 at 6:39
  • This returns most of the Tag matched perfectly but What If some post matched 2, 2 tags and I want to change these 2,2 tags matched post order too.i.e order by date or views count desc or asc order?
    – Ghost
    Feb 11, 2023 at 6:44
  • Please read the WP_Query documentation. You can use order by date directly in WP_Query. This will be added with the other order and orderby you provide in the $args. developer.wordpress.org/reference/classes/wp_query/…
    – Fayaz
    Feb 11, 2023 at 8:20
  • 1
    I've updated the answer with additional orderby. date is supported by WP_Query by default, There are many other supported orderby values. However, WordPress doesn't implement view count by default. So that is a completely separate question. So it cannot be discussed in this question.
    – Fayaz
    Feb 11, 2023 at 8:44
  • 1
    Great answer @Fayaz! BTW, I think it could be even more beautiful, if instead of order_by_most_tags argument, you could implement it using the order and orderby arguments. In that case, we could use it like 'orderby' => array( 'most_tags' => 'DESC', 'comment_count' => 'DESC', 'date' => 'ASC' ). Please don't bother if it is too much additional work. I think the solution is already excellent.
    – Karma
    Feb 11, 2023 at 22:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.