How To List Top Commentators in WordPress Without A Plugin
I have always been a big fan of extended functionality, but never a big fan of means to extend by using plugins. Not that plugins are bad, but usually they have the tendency of slowing things down, and that is something not to commend. In the other hand, for the ‘unfortunate’ luck of WordPress being so popular, there are so many plugins in the repository, I suspect there is a developer in the world who hasn’t created his own. I’m thinking whether my grand-mother has developed one yet. Not everything that shines is gold.
After reading this write up by Gines, I suggested not to always rely on plugins, instead try to avoid them where possible. Although he makes a good point when says:
But for our friends who’s not in to coding, queries, development, plugins will have to do.
Anyway, as I challenged myself towards it, I jumped on and started to write my own Top Commentators function so I could list them on the sidebar. Surprisingly Matt listed as second top commenter, and it is a bit odd you know.
Now if you (as I) want to list your blogs Top Commentators lets go on and do some coding. Fire up your favorite code editor and open your themes function.php file in it.
Initially we need to create our function, I called it top_commentators() as its intention clearly indicates.
function top_commentators($show = 5){
Next step we globalize $wpdb class for our use, and execute our query to retrieve the required information from the database.
//Globalize $wpdb class
global $wpdb;
//check for function results cache
if (false === ( $comments = get_transient('comments') ) ) {
//Execute Query and Store Results
$comments = $wpdb->get_results('
SELECT COUNT(comment_author_email) AS comments_count,
comment_author_email, comment_author, comment_author_url
FROM '.$wpdb->comments.'
WHERE
user_id != 1 AND comment_approved = 1
AND comment_type = "" AND comment_author_email != ""
GROUP BY comment_author_email
ORDER BY comments_count DESC, comment_author ASC
LIMIT '.$show );
//Store cache in Database with 24 hours expiration time
set_transient('comments', $comments, 60*60*24);
}
Some explanation might be necessary here. Note where the WHERE clause starts and the following code. I apply some restrictions here, first I use user_id != 1 to filter myself off the list, then I check comment_approved = 1 whether the comment is approved, then again I tell to look for comments comment_type = “” that are actual comments, not trackbacks or pingbacks and = “” looks for comments by default, no need to write the word within the quotes. And finally I ask comment_author_email != “” not to be blank. You can remove some or apply more if you wish.
The code from the above block, will execute the SQL statement and store it in the $comments variable as an array.
Finally, we need to organize our information structure, how we want to list them.
I chose div > p over ul > li combination just because I can.
//Prepare HTML Output Structure
$output = '<div id="topcommentators"><h3>Top Commentators</h3>';
//Loop through found records
foreach($comments as $comment){
//Check if Commenter has provided a Website URL
//if YES, link his/her info his/her site URL
if($comment->comment_author_url){
$output .= '<p><a href="'.$comment->comment_author_url.'" title="'.$comment->comment_author.'">'.get_avatar($comment->comment_author_email, $size='24', $default='', $comment->comment_author).' '.$comment->comment_author.' ('.$comment->comments_count.')</a></p>'.PHP_EOL;
}
else { //If Commenter hasn't provided an URL to his site then display the rest of the information
$output .= '<p>'.get_avatar($comment->comment_author_email, $size='24', $default='', $comment->comment_author).' '.$comment->comment_author.' ('.$comment->comments_count.')</p>'.PHP_EOL;
}
}
//End HTML Output
$output .= '</div>';
//Print results in the browser
echo $output;
}
Basically, this is the easy part. After we retrieve the information we store the information into the $output variable and finally we echo everything to its own place. I was forced to do an if {} else check, because $comment->comment_author_url would return home blogs URL in case the comment author didn’t put his own. That was weird though. And we show the commentators’ Gravatar if there is one, or the default is returned.
Now, of course this doesn’t do anything on it’s own, so we need to call it anywhere we want to show our valued TOP Commentators who make our blogs look so neat and useful, and trustworthy, and worth spending our time, and much more.
Go ahead now, put this <?php top_commentators(); ?> wherever you want to list your TOP FIVE. I decided sidebar, yours can be footer or anywhere else on your blog. You can, however, alter the returned number of top comment authors to your satisfaction, say 10 for instance, simply by adding a desired value to the function:
<?php top_commentators(10); ?>
And last but doubtlessly not least, comes the CSS part where we “fancyfy” the output. Here’s what I used, change it to fit you best.
div#topcommentors p {
margin: 0 0 5px;
padding: 0;
border-bottom: 1px dashed #ccc;
}
div#topcommentors p:hover { background: #f1f1f1; }
That was all it took to get what we wanted, in our own without activating YAMP (Yet Another More Plugin).
Let me know if it worked for you, if any troubles arise while trying to realize this on your own, just send me a nudge in the Comment form below. I’d try to assist you as best as I can.
And this would be the complete top_commentators() function code, just in case.
//Top Comment Authors
function top_commentators($show = 5){
//Globalize $wpdb class
global $wpdb;
//check for function results cache
if (false === ( $comments = get_transient('comments') ) ) {
//Execute Query and Store Results
$comments = $wpdb->get_results('
SELECT COUNT(comment_author_email) AS comments_count,
comment_author_email, comment_author, comment_author_url
FROM '.$wpdb->comments.'
WHERE
user_id != 1 AND comment_approved = 1
AND comment_type = "" AND comment_author_email != ""
GROUP BY comment_author_email
ORDER BY comments_count DESC, comment_author ASC
LIMIT '.$show );
//Store cache in Database with 24 hours expiration time
set_transient('comments', $comments, 60*60*24);
}
//Prepare HTML Output Structure
$output = '<div id="topcommentors"><h3>Top Commentors</h3>';
//Loop through found records
foreach($comments as $comment){
//Check if Commenter has provided a Website URL
//if TRUE, show URL and the rest of the information
if($comment->comment_author_url){
$output .= '<p><a href="'.$comment->comment_author_url.'" title="'.$comment->comment_author.'">'.get_avatar($comment->comment_author_email, $size='24', $default='', $comment->comment_author).' '.$comment->comment_author.' ('.$comment->comments_count.')</a></p>'.PHP_EOL;
}
else { //If Commenter hasn't provided an URL to his site then display the rest of the information
$output .= '<p>'.get_avatar($comment->comment_author_email, $size='24', $default='', $comment->comment_author).' '.$comment->comment_author.' ('.$comment->comments_count.')</p>'.PHP_EOL;
}
}
//End HTML Output
$output .= '</div>';
//Print results in the browser
echo $output;
} //End Function
Download this function in a file: Function Top Commentators
Update: Thanks to our friend Pepe for asking in the comments, I’ve added two more lines in the second and final code blocks. Now it checks for cache if exists in Database, if not it’ll set a cache and an expiration time. This means the query will be executed once in 24 hours. You should see two new rows in wp_options table _transient_timeout_comments and _transient_comments.


Hey Arian,
Very nice and concise tutorial. I’d definitely give a go. I’ll also try to incorporate some of the plugins directly instead of as a plugin (eg Twitter username into the comment form). This and many more as I go along with the redesign.
I’ve switched to Thematic so my site now looks dull and lonely. But not for long as I start the journey next week.
BTW, thanks for the mention. Have a good one.
Hi Gines,
Thanks for the appreciation.
Let me know if you need a hand with anything on your much talked journey.
Have a good one too.
What about caching the output?
If I’m not mistaken, this query will repeat each time somebody loads a page, hammering the DB on “big” sites.. is there any way to cache the whole thing or, for example, run the query just once a day or in a determined time period?
Regards
Good catch Pepe.
I’ve made the changes above. Now it is cached for 24 hours.
Thanks for your time.
Thank you very much Arian.
I’ve spent some time cracking some ideas that came after this piece of code, maybe you can see the light at the end of the tunnel.
First, I wanted to add a time limitation for the list, say top comentators “this year”, so, inserted a new line after 17:
AND DATE_FORMAT(comment_date, “%Y”) = DATE_FORMAT(CURDATE(), “%Y”)
It works!
Also I wanted to exclude the blog admins/editors, I did it using raw names instead of emails:
AND comment_author NOT IN (“Admin1″, “Admin2″, “Admin3″)
Im now trying to understand why this works as intended in the blog’s index but not in single, because in single posts it displays the list unfiltered (no date filter, no name filter).. maybe Im doing something wrong… after all, Im just an uber amateur wp coder :P
Also, before checking your solution, I started with some wp_cache stuff to store the query, I dont know wich one will be more efficient and cheaper for the machine, something like this:
global $wpdb;
$cached = wp_cache_get($tc_query);
if ($cached == false) {
$comments = $wpdb->get_results (‘blablabla big query’);
wp_cache_set($tc_query, $comments);
} /*if $cached == false */ else {
$comments = $cached;
}; /*else*/
Kind regards and thank you again.
Hey there again Pepe.
Last things first now,
I used Transient API instead of WP Cache object because the first one stored the cache in the DB and WP Cache only resides in the memory as long as the visitor keeps your blog page opened. They changed WP Cache’s persistent caching ability since WP 2.5, that’s why I chose the first over the second. You have my reason, choose what you think it’s best.
Next thing, regarding your issue with the filters not being applied when you load a single post, I can’t really tell you much right now, as I’m not aware of what exact code are you using. I can just suggest you use single quotes instead of double quotes here: AND comment_author NOT IN (‘Admin1′, ‘Admin2′, ‘Admin3′)
I would gladly have a look on your function though. You can use http://pastebin.com/ to paste your code and share the link with me.
or email it over to me (arianxh [at] live [dot] com)
Hope you settle that trouble or I will have to. :)
Cheers.
Hi Arian,
http://pastebin.com/w30Jshjd
The error I talked about in my last comment is solved now, some extra conditional tags in my code where causing it :P.
Im working on add the time filter as a parameter for the function.. still work in progress (check pastebin).
Thank you for the clarification about wp_cache, as you say, the Transient API works better for this code.
Kind Regards,
P.
Hey Pepe,
glad you worked it out,
I had a quick look on your changes,
and I think I’ve spotted your error
just replace your
AND DATE_FORMAT(comment_date, “%Y”) = DATE_FORMAT(CURDATE(), “%Y”)
with
AND DATE_FORMAT(comment_date, comment_date) = DATE_FORMAT(CURDATE(), “‘.$tf.’”)
this selects comments based on the current year.
P.S. my answer was irrelevant, now I understand what you’re looking for. I’ll get back to you with the solution, it is going to be a little more challenging now. You’re being greedy now Pepe. :)I fixed it, it works now. Don’t get confused if there are no comments returned because sometimes the dates don’t match.