Wie meine Kommentatoren wissen, zeige ich bei jedem Kommentar an, wie viele Kommentare dieser Kommentator hier bisher geschrieben hat (genauer gesagt: wie viele unter demselben Namen abgegeben wurden; mehr zu dieser Unterscheidung später). Über die Details und die Neuerungen Ende letzten Jahres wollte ich schon länger mal schreiben, zumal auch manche andere davon profitieren könnten…
Ursprünglich geschah dies – natürlich ohne extra Plugin (und ohne Abacus ) – mit einer einfachen Datenbankabfrage, die in der Kommentarausgabe aufgerufen wird:
$c_count = (int) $wpdb->get_var(
"SELECT COUNT(*) AS comments
FROM $wpdb->comments
WHERE comment_author='".esc_sql($comment->comment_author)."'
AND comment_type=''
AND comment_approved='1'");
(Hinweis: esc_sql
ab WP 3.6; zuvor war’s $wpdb->escape
)
Das Problem dabei: eine Datenbankabfrage pro Kommentar. Das ist kein so großes Problem, wenn es wenige Kommentare zum Beitrag oder insgesamt wenige Kommentare gibt. Aber je mehr Kommentare, je mehr verschiedene Kommentatoren und je seltenere Kommentatoren es gibt, umso mehr hat der Datenbankserver zu ackern und umso weniger kann ein Query-Cache – den wohl jeder MySQL-Server hat – oder ein DB-Cache-Plugin ausrichten. Und wenn der Beitrag zwar öfters, aber nicht ständig aufgerufen wird, fallen die Anfragen oder der Beitrag selbst auch immer wieder aus dem Cache raus.
Wenn ein Blog schon einige tausend Kommentare hat, ist diese Abfrage (gerade bei Shared Hosting) recht „teuer“ – ich hatte damals bei Tests Zeiten von bis zu 300 ms pro Anfrage (was ich in aktuellen Tests aber nicht mehr reproduzieren konnte, da waren’s nur ca. 30; keine Ahnung, was sich da geändert hat), was sich bei einem von vielen Leuten vielkommentierten Beitrag so darstellen kann, dass man sehen konnte, wie die Kommentare nach und nach „hingeblättert“ wurden…
Deswegen hab ich mir – nach einem kurzen Test mit einem simplen internen Cache, der sicher noch etwas schneller als eine Anfrage an den MySQL-DB-Cache ist – eine Lösung gebastelt, bei der alle Kommentaranzahlen für den jeweiligen Beitrag auf einmal eingelesen werden, was meinen Tests zufolge nicht wesentlich teurer ist als eine einzelne Abfrage der oben genannten Art:
$ag_number_of_comments_cache = array();
$ag_number_of_comments_inited = false;
function ag_number_of_comments ($comment, $before="",$after="") {
global $wpdb,$ag_number_of_comments_cache,$ag_number_of_comments_inited;
if (!$ag_number_of_comments_inited)
ag_number_of_comments_post ((int) $comment->comment_post_ID);
if ($comment->comment_type=="") {
$c_count = (int) $ag_number_of_comments_cache[ mb_strtolower($comment->comment_author) ];
echo $before."<span class=\"comments-by-author\">".($c_count==1?__('1 Comment'):$c_count." ".__('Comments'))."</span>".$after;
}
}
function ag_number_of_comments_post ($post_ID) {
global $wpdb,$wp_query,$ag_number_of_comments_cache,$ag_number_of_comments_inited;
if ($post_ID==0) return;
$ag_number_of_comments_inited = true;
$comments = $wp_query->comments;
$authors_sql = '0';
foreach ($comments as $c) {
if ( ($c->comment_type=='') && ($c->comment_approved=='1') ) {
$authors_sql .= " OR comment_author='".esc_sql(mb_strtolower($c->comment_author))."'";
}
}
$authorcounts = $wpdb->get_results(
"SELECT comment_author, COUNT(*) as comments
FROM $wpdb->comments
WHERE comment_type=''
AND comment_approved='1'
AND ($authors_sql)
GROUP BY comment_author");
foreach ($authorcounts as $a) {
$ag_number_of_comments_cache[mb_strtolower($a->comment_author)] = (int) $a->comments;
}
}
Eingefügt wird dieser Code am besten natürlich in die functions.php
des Themes (auf jeden Fall nach dem <?php
am Anfang und vor dem ?>
am Ende der Datei!) und aufgerufen dann an der geeigneten bzw. gewünschten Stelle in der Kommentarausgabeschleife in der comments.php
, etwa vor oder nach der Datumsausgabe, mit diesem PHP-Funktionsaufruf:
ag_number_of_comments ($comment);
Bzw. wenn es mitten im HTML-Code stehen soll, natürlich in PHP-Tags eingeschlossen:
<?php ag_number_of_comments ($comment); ?>
Mit den beiden zusätzlichen Parametern kann man noch etwas davor- und dahintergestellten Text angeben, für eine eingeklammerte Ausgabe etwa:
ag_number_of_comments ($comment, " (",") ");
Im Theme-CSS lässt sich das ganze dann mit der Klasse .comments-by-author
ansprechen und stylen, falls gewünscht.
Zur eingangs erwähnten Zählung der Kommentare nur nach dem Namen möchte ich noch sagen, dass ich das zwar auch nicht optimal finde, da so mitunter verschiedene Leute, die mit demselben, i.d.R. „normalen“ Namen kommentieren, nicht unterschieden werden können. Nun könnte man zusätzlich die E-Mail-Adresse als Unterscheidungskriterium hinzufügen – doch wenn diese sich ändert (was bei ein paar Kommentatoren schon der Fall war), ist das Ergebnis auch nicht das gewünschte, genausowenig wie bei der URL, wo noch öfter „falsche Unterschiede“ auftreten, weil mal ein „/“ am Ende angegeben wurde und mal nicht. Deswegen belasse ich es beim Namen als Kriterium.
Übrigens wird die Groß-/Kleinschreibung auch nicht unterschieden – schon der Vergleich in MySQL kümmert sich nicht darum, weshalb eine entsprechende Umwandlung in PHP mittels mb_strtolower
nötig ist; diese Funktion, die ordentlich mit Umlauten und Sonderzeichen umgehen kann, gibt’s ab PHP 4.3.0 (was heutzutage kein Problem sein sollte).
Nun denn, vielleicht hilft dieser Tip dem einen oder anderen. Es mag nicht der perfekte Code sein, denn auch wenn ich manchmal besserwisserisch korrigieren kann, bin ich kein PHP-Guru. Also sind Verbesserungsvorschläge sowie Anregungen und Fragen natürlich willkommen.
Foto: Monika Adamczyk – Fotolia.com