Sticky Comments

I was curious about any existing plugins and searched the plugin directory. There exists an old Sticky Comments plugin, I’m not related to it. It seems to use a sticky meta key.

It uses a LEFT JOIN query but creates it’s own version of the whole comments_template() core function, to override the current query. One would need to update that custom function, if the core function changes. This was most likely done because of the hard coded SQL queries that this function contained.

Now, in WordPress 4.3.1, the comments_template() function only contains a single get_comments() call and we can therefore use a single filter instead to override it.

Now here are few ideas how to do that:

Method #1

We could use the sticky comment type to mark the sticky comments.

Then we could use the pre_get_comments hook to adjust the ordering:

add_action( 'pre_get_comments', 'wpse_comment_stickies_v1' ); 

function wpse_comment_stickies_v1( \WP_Comment_Query $q )
{
    // Only run it once
    remove_action( current_action(), __FUNCTION__ );

    // Modify the ordering, so sticky comments are at the top
    $q->query_vars[ 'orderby'] = [
        'comment_type'      => 'DESC',
        'comment_date_gmt'  => 'ASC',
        'comment_ID'        => 'ASC'
    ];

}

where you’ve to add the line:

add_action( 'pre_get_comments', 'wpse_comment_stickies_v1' ); 

just before the comments_template() call in your theme.

The array ordering was added in WordPress 4.2 but the Codex on WP_Comment_Query doesn’t mention it.

Method #2

This one is similar as in #1, but here we use the comments_clauses filter to modify the ordering of the comments:

add_filter( 'comments_clauses', 'wpse_comment_stickies_v2' ); 

function wpse_comment_stickies_v2( $clauses )
{
    global $wpdb;

    // Only run it once
    remove_filter( current_filter(), __FUNCTION__ );

    // Modify the ordering, so sticky comments are at the top                
    $clauses['orderby'] = $wpdb->comments . '.comment_type DESC, ' . $clauses['orderby'];

    return $clauses;
}

Method #3

We could use the comment meta as well. If we mark the sticky comments with the sticky comment meta, then we can modify the ordering with:

add_action( 'comments_clauses', 'wpse_stickies_v3' ); 

function wpse_stickies_v3( $clauses )
{
    global $wpdb;

    // LEFT JOIN the comments table and the comments meta table
    $clauses['join'] .= " 
        LEFT JOIN {$wpdb->commentmeta} wpsecm 
        ON ( wpsecm.comment_id = {$wpdb->comments}.comment_ID AND wpsecm.meta_key = 'sticky' ) ";

    // Order by the meta key 
    $clauses['orderby'] = 'wpsecm.meta_key DESC, ' . $clauses['orderby'];
    return $clauses;
}

Method #4

This is similar to #3, but where we only modify the orderby clause:

add_action( 'comments_clauses', 'wpse_stickies_v4' ); 

function wpse_stickies_v4( $clauses )
{
    global $wpdb;
    $orderby = [];
    $orderby[] = "
        (   SELECT COUNT( comment_ID ) 
            FROM {$wpdb->commentmeta} wpsecm 
            WHERE wpsecm.comment_id = {$wpdb->comments}.comment_ID 
                AND wpsecm.meta_key = 'sticky' 
        ) DESC ";
    $orderby[] = $clauses['orderby'];
    $clauses['orderby'] = join( ',', $orderby );

    return $clauses;
}

Notes

Sometimes I wish there was an argument filter in comments template(), to make it easier to only target it’s query, because you don’t want to mess with other get_comments() calls e.g. in the widgets.

I created a ticket #34442 with some suggestions, that would make it easier to target this query from a plugin.

In WordPress 4.4 the get_comments() call will be replaced with:

$comment_query = new WP_Comment_Query( $comment_args );

but that will not change much here, since get_comments() is just a simple WP_Comment_Query wrapper.

I haven’t mentioned here how one could modify the UI to mark the sticky comments.
But you can e.g. check out this old plugin, I mentioned earlier, for ideas on that matter.

Leave a Comment