简体   繁体   中英

Ordering Wordpress posts by most recent comment

I'm wanting to order Wordpress posts by the most recent comment. To the best of my knowledge this isn't possible using the WP_Query object, and would require a custom $wpdb query, which I can easily write. However, I then don't know how to setup the loop to run off this object.

Can anyone help?

Assign

select wp_posts.*, max(comment_date) as max_comment_date
from $wpdb->posts wp_posts
right join $wpdb->comments
on id = comment_post_id
group by ID
order by max_comment_date desc
limit 10

to some variable $query. You can fiddle around with the 10 or the query itself. (I'm no SQL optimization ninja.) Then your code will look something like

<?php
     $results = $wpdb->get_results($query) or die('!');
     foreach ($results as $result):
?>
[insert template here]
<?php endforeach ?>

This pattern is covered in more depth by the Codex .

I used a simpler, portion of a native WP in function. hope it helps and some one can continue to develop. Here is a simplified version that shows the title & excerpt of the post along with the comment content & author from the latest commented posts using get_comments.

    $args = array(
        'status' => 'approve',
        'number' => 6,
        'order' => 'DESC'
    );
    $comments = get_comments($args);

    foreach($comments as $comment) : $count++;

            $post_args = array(
                'post_type' => 'post',
                'p' => $comment->comment_post_ID,
                'posts_per_page' => 1
                );

            $posts = get_posts($post_args);

            foreach($posts as $post) : setup_postdata($post);

                the_title();
                the_excerpt();

            endforeach;

        echo $comment->comment_content;     
        echo $comment->comment_author;

    endforeach;

This is an old question, but I had the same issue and found a much cleaner way to do this, so I'm posting it here in case it helps anyone.

If you use the posts_clauses filter you can then just modify the main query and still use The Loop and all the regular loop functions.

function intercept_query_clauses( $pieces ) {
    global $wpdb;

    $pieces['fields'] = "wp_posts.*,
    (
        select max(comment_date)
        from " . $wpdb->comments ." wpc
        where wpc.comment_post_id = wp_posts.id AND wpc.comment_approved = 1
    ) as mcomment_date";
    $pieces['orderby'] = "mcomment_date desc";

    return $pieces;
}

add_filter( 'posts_clauses', 'intercept_query_clauses', 20, 1 );

Note that I changed the sql slightly for my own purposes, but the general concept is the same.

OK guys,

A lot of great answers here, but obviously nobody's taken the time to test them.

Hao Lian gets the credit for the first best original answer, but unfortunately his code doesn't show posts without comments.

Captain Keytar is on the right track, but his code will display every single post and attachment as a separate result.

Here is a modified version of Captain Keytar but it limits the results to the type 'post'.. that has been published (to avoid getting drafts!!)

    select wp_posts.*,
    coalesce(
        (
            select max(comment_date)
            from $wpdb->comments wpc
            where wpc.comment_post_id = wp_posts.id
        ),
        wp_posts.post_date
    ) as mcomment_date
    from $wpdb->posts wp_posts
    where post_type = 'post'
    and post_status = 'publish' 
    order by mcomment_date desc
    limit 10

As an addendum to Hao Lian's answer, if you use the following query:

select wp_posts.*,
coalesce(
    (
        select max(comment_date)
        from $wpdb->comments wpc
        where wpc.comment_post_id = wp_posts.id
    ),
    wp_posts.post_date
) as mcomment_date
from $wpdb->posts wp_posts
order by mcomment_date desc
limit 10

This mixes in posts that don't have comments yet, and sorts them by post_date and max(comment_date).

Code suggested by Hao Lian works perfect except for the fact that we should add the following WHERE clause to avoid pulling POST with comment_count = 0 , this situation is caused by spam comments.

The WHERE clause to add is as follows:

WHERE comment_approved = '1' AND comment_type = '' AND post_password = ''

Complete code after adding the where clause shoud look like following:

select wp_posts.*, max(comment_date) as comment_date
from wp_posts 
right join wp_comments on id = comment_post_id
WHERE comment_approved = '1' AND comment_type = '' AND post_password = ''
group by ID    
order by comment_date desc
limit 6

This can be done by combining WP_Comment_Query with WP_Query, like this:

// For performance, limit the number of queried comments, 
// but make it be something big enough to account for "duplicate" posts.

$comments_query = new WP_Comment_Query;
$comments = $comments_query->query( array(   'number' => '100'  ) );

if ( $comments ) {
    foreach ( $comments as $comment ) {

// You'll want to convert the dates from string to integer so you can sort them out later
$comment_utf = strtotime($comment->comment_date);

// Build an array of post IDs with the date of the last published comment
$latest_comments[$comment->comment_post_ID] = $comment_utf;
    }}

// Sort the array by date
arsort($latest_comments); foreach ($latest_comments as $key => $value) {    $posts_ordered[] = $key; }

// The nice thing is that WP_Query will remove duplicates by default
$args = array ( 'posts_per_page'         => '10',   'post__in'  => $posts_ordered, 'orderby' => 'post__in');
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
    while ( $query->have_posts() ) {
        $query->the_post();

// Do your stuff (add the template or whatever)

// If you want to add the comment itself, use this:
$comments = get_comments(array('number' => '1', 'post_id' => $post->ID));
foreach($comments as $comment) :
    echo $comment->comment_content;
endforeach;

// That's about it
    }}
wp_reset_postdata();

I'm thinking that adding in the max function will screw up your results. MySQL isn't going to pull the max from each one. It's going to pull the max from the full set. This is the query that'll get you your results:

select wp_posts.*, comment_date
from $wpdb->posts wp_posts
right join $wpdb->comments
on id = comment_post_id
group by ID
order by comment_date desc
limit 10

After that, if you want to follow WP convention, use this, and then you can use the functions that most of your templates are using (based on the loop):

$results = $wpdb->get_results($query) or die('!');
     foreach ($results as $post):
    setup_postdata($post);

Get 3 newest comments for custom post type 'question' regardless of approvement:

global $wpdb;

$results = $wpdb->get_results(
    "
    SELECT wp_posts.ID, MAX(comment_date) AS max_comment_date
    FROM wp_posts
    RIGHT JOIN wp_comments
    ON id = comment_post_id
    WHERE wp_posts.post_type = 'question'
    AND wp_posts.post_status = 'publish'
    GROUP BY ID
    ORDER BY max_comment_date DESC
    LIMIT 3
    "
);

foreach ($results as $result) {
    $posts_arr[] = $result->ID;
}

$args = array(
    'post_type' => 'question',
    'post__in'  => $posts_arr,
    'orderby'  => 'post__in',
);

$the_query = new WP_Query( $args );

Using Lucian's BEAUTIFUL solution, I needed to alter/filter the existing WP_Query to sort posts by the latest comment. Here's the code, tested & works perfectly:

$comments_query = new WP_Comment_Query;
$comments       = $comments_query->query( array(   'number' => '100'  ) );

if ( $comments ) {
  foreach ( $comments as $comment ) {
    $comment_utf = strtotime($comment->comment_date);
    $latest_comments[$comment->comment_post_ID] = $comment_utf;
  }

  // Sort the array by date
  arsort( $latest_comments );
  foreach( $latest_comments as $key => $value ) {
    $posts_ordered[] = $key;
  }

  $query->set( 'post__in', $posts_ordered );
  $query->set( 'orderby', 'post__in' );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM