Comment count on article/blog listing

Hi, I’ve been looking to add a comment count next to article titles in the blog listing, but it doesn’t look like that data is exposed by page.find(). The Comments plugin gets that count in the admin panel with the following function:

/**
 * Return the latest commented pages
 */
private function fetchPages() {
    $files = [];
    $files = $this->getFilesOrderedByModifiedDate();

    $pages = [];

    foreach($files as $file) {
        $pages[] = [
            'title' => $file->data['title'],
            'commentsCount' => count($file->data['comments']),
            'lastCommentDate' => date('D, d M Y H:i:s', $file->modifiedDate)
        ];
    }

    return $pages;
}

Is there any way I could obtain a similar count for a specific page? I have a workaround that works by using p.title from page.find() to cross-reference pages.title from fetchPages(), but that forces me to loop through every commented page on the site:

	{% set articles = page.find('/news').children.order('date', 'desc').slice(0,3) %}
	{% for p in articles|slice(0,3) %}
    <div class="latest-post">
        <h2><a href="{{p.url}}">{{ p.title|e }}</a></h2>&nbsp;&mdash;&nbsp;{{ p.date|date("M j, Y") }}
		{% for page in grav.twig.pages %}
			{% if page.title == p.title %}
				&nbsp;&ndash;&nbsp;{{page.commentsCount}} comment{% if page.commentsCount > 1 %}s{% endif %}
			{% endif %}	
		{% endfor %}
		{{p.content}}
    </div>
	{% endfor %}

I couldn’t find a way to find an array key from a value to avoid looping (something like get_key_for_value(value, array)). What’s more, I’m not sure whether Grav allows duplicate names for pages, but I feel like searching based on route would be safer. Any help would be very appreciated.

OK I think I follow this.

A few Twig things:

  • you’ve sliced that page collection twice (lines 1 & 2) - I’d recommend removing all that slicing and ordering while you debug this
  • look into using {% for ... if ... %} rather than separate blocks, you can even add an else block - BUT! you don’t need any of this here as per my next point
  • your inner loop seems completely redundant to me - don’t you already have access to p.commentsCount in your outer loop?
  • Grav has a custom Twig filter called pluralize for the part where you add an ‘s’, so you can simply say p.commentsCount 'comment'|pluralize(p.commentsCount)

You don’t need this either if you remove the inner loop, but you could have reliably matched on the unique url property rather than title.

Does that get you back on track?

Oh, thanks. I saw that filter during my searches but didn’t bother to implement it at the time.

Nope, that doesn’t display anything. As far as I can tell (which is not very far), commentsCount comes specifically from the Comments plugin so page.find doesn’t have a way to access it.

fetchPages() doesn’t give me a URL/route property to work with, but more importantly I just found out that it only fetches comments from the past 7 days (by virtue of getFilesOrderedByModifiedDate() having that limitation), which doesn’t work for my use case. Ideally, I’d rather have a way to target a specific comment thread, but I don’t know how I would make those look-ups from Twig without having to pregenerate results for every existing page at every page load anyway, which is insane. It would be so trivial if I could just use PHP =/

For the time being, I think I have it working with a customized copy of fetchPages() and getFilesOrderedByModifiedDate() that only collects comments from a specific, hard-coded folder regardless of date. I can then iterate through only those in my template and compare the URLs since I changed the standard comment format to include the route as part of the parameters. It still feels very hacky, but at least it cuts down on a lot of iteration. It’s still not ideal if I wanted to show comment counts on blog listings for several categories, but since I only need it for my news feed for the time being, it is serviceable.

With your advice, the output is now as follows:

	{% set articles = page.find('/news').children.order('date', 'desc').slice(0,3) %}
	{% for p in articles %}
    <div class="latest-post">
        <h2><a href="{{p.url}}">{{ p.title|e }}</a></h2>&nbsp;&mdash;&nbsp;{{ p.date|date("M j, Y") }}
		{% for newspage in grav.twig.newspages if base_url_relative ~ newspage.route == p.url %}
			&nbsp;&ndash;&nbsp;<a href="{{p.url}}#comments">{{ newspage.commentsCount }} {{ 'comment'|pluralize(newspage.commentsCount) }}</a>
		{% endfor %}
        {{p.content}}
    </div>
	{% endfor %}

Thanks for the pointers! (Though I’m still very interested if anyone can think of a way to target specific comment pages from Twig.)