Textpattern tips, tutorials and code snippets

Most commented articles

To generate a list of ‘most popular’ articles (as judged by the number of comments received) requires a little cunning because comments and articles are stored separately inside Textpattern. There is a native solution already posted, but this can be extended a little further to only show comments within a certain time frame (see below). It’s a tiny bit more efficient as well because it only calls the database once.

To begin, let’s use the catch-all plugin smd_query, as follows:

<txp:smd_query
     query="SELECT ID, Title, Count(parentid) AS num
        FROM txp_discuss AS com
        LEFT JOIN textpattern AS txp
        ON txp.ID = com.parentid
        WHERE com.visible = 1
        GROUP BY com.parentid
        ORDER BY num desc"
     wraptag="ul" break="li">
   <txp:permlink id="{ID}">{Title}</txp:permlink> [ Comments: {num} ]
</txp:smd_query>

Piece by piece

What is going on here? Let’s dissect it a little to find out.

First we get hold of the article’s ID (so we can use it in the <txp:permlink /> tag), its title and the number of comments associated with this article’s ID. We’re calling the comment count num as a shorthand:

SELECT ID, Title, Count(parentid) AS num

Next, is simply some SQL magic to link the txp_discuss table (which holds comments) with the textpattern table (which holds articles). Note again that we’re using the AS operator to let our query know we’re using a shorthand to denote each table’s name. Essentially, this part grabs only those articles that have comments lodged against them:

FROM txp_discuss AS com
LEFT JOIN textpattern AS txp
ON txp.ID = com.parentid

We only want visible comments, not unmoderated or blocked ones:

WHERE com.visible = 1

The clever bit comes next. This simply aggregates the results so that there is only one row returned for each article, with its associated number of comments. Without this you would get the same article repeated, once for each comment that has been made against it:

GROUP BY com.parentid

Finally, sort them in descending order of comment count (num in our case):

ORDER BY num desc

Et voila! The rest of the stuff in the tag’s container just formats a <ul> list with each permlinked article and its number of comments.

Time-based comments

An extension of this technique is to only count articles that have received comments in the last N days, for example comments made in the last week.

For this we need to add just one more line to the query’s WHERE clause:

com.posted > ADDDATE(CURDATE(), INTERVAL -7 DAY)

ADDDATE is a MySQL function that simply takes its first argument (CURDATE() returns the current date) and adds the specified INTERVAL to it. In this case we’re adding -7 days, which does the same as subtracting 7 days. You could use the SUBDATE() function if you preferred to make things clearer.

The INTERVAL can be a variety of things such as INTERVAL -1 MONTH, even HOURs if you wanted.

Incorporating this extra line into our code (and adding a LIMIT so we only get a specific number of results) gives us the top 10 articles by the number of comments received in the past week:

<txp:smd_query
     query="SELECT ID, Title, Count(parentid) AS num
        FROM txp_discuss AS com
        LEFT JOIN textpattern AS txp
        ON txp.ID = com.parentid
        WHERE com.posted > ADDDATE(CURDATE(), INTERVAL -7 DAY)
        AND com.visible = 1
        GROUP BY com.parentid
        ORDER BY num desc
        LIMIT 10"
     wraptag="ul" break="li">
   <txp:permlink id="{ID}">{Title}</txp:permlink> [ Comments: {num} ]
</txp:smd_query>

2 Comments Comment feed

Great tip, Stef.
As a MySQL-ignorant, it really helps to learn a few things on MySQL obscurantism.

Thanks.

Sweet tutorial, this will come in very handy on the community-driven website I’m working on. It’s would be nice though to have an article listing that can display details like article category and custom fields, in addition to the title and comment count you allow with this method. I know next to nothing about making SQL queries, but I’m guessing this method isn’t looking up the category during the query, and so it can’t do anything when you use {Category1} to try and display it in an article form. If you have any idea how I can make this happen, I would greatly appreciate it. Thank you!

Add a comment

Use Textile help to style your comments