Kommentar-Anzahl anzeigen (ohne Plugin)

Kinder mit Abacus 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:1

$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.php2, 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

  1. Das Syntax-Highlighting des Codes hab ich hier online erstellt, musste dann aber noch die einzelnen Anführungszeichen durch &#39; ersetzen, damit WordPress sie nicht in ihre hübsche Variante umwandelt. Vielleicht finde ich fürs nächste Mal einen Highlighter, der auch das noch berücksichtigt… []
  2. Wenn ihr dort nur einen Aufruf der Funktion wp_list_comments() ohne Callback-Parameter findet, wird es etwas komplizierter – dann ließe es sich vielleicht in den Filter get_comment_date oder get_comment_author einhängen und somit vor/nach diesen ausgeben, aber das ist zu viel für diesen Beitrag; falls jemand ein Plugin dafür kennt – oder für die, die generell nicht gern im Code rumpfriemeln –, könnt ihr ja Bescheid sagen. []

15 Kommentare
1 Trackback

  1. T

    Guru biste vielleicht keiner, aber dennoch ist das eine ganz ansehentliche Konstruktion ohne weiteren Plugin-Blasebalg. Mal sehen wann ich das verwerten kann. Besten Dank für den Tipp ;)

  2. jL

    Ah, sehr schön, das wird sich vor allem beim Rätseln positiv auswirken :)

    • c

      Grad beim Rätseln hilft auch schon der DB-Cache, weil es ja wenige Rätsler sind, die viele Kommentare schreiben. Ich hatte es bei mir v.a. bei den 2012-Prophezeiungen und der Wahrsagerin bemerkt…

  3. M

    count(*) ist grundsätzlich immer nur die Notlösung wenn wirklich kein einzelnes Feld gewählt werden kann. Vielleicht kannst du das noch ändern, dann wird die Abfrage schneller

    • c

      Ich wüsste nicht, wie – außer dass man die Zahlen irgendwo anders extra speichert, denn ein eigenes Feld gibt’s so nicht. Oder hast du eine andere Idee?

  4. F

    WordPress stellt für diesen Zweck eine Lösung bereit – get_comment_count();, man muss also keinen Zusatzcode verwenden, siehe Beitrag dazu.

  5. R

    Hallo :) Entschuldige bitte, dass ich diesen alten Beitrag nochmal hervorzaubere, aber es ist jüngst eine Frage zu Deinem „Plugin“ entstanden. Ich nutze es immer noch sehr erfolgreich, den von Dir entwickelten Code-Schnipsel. Leider benutzt die Funktion noch eine alten Datenbankcode, der seit WordPress 3.6. nicht mehr benutzt wird. „wpdb::escape()“

    Kann ich den betreffenden Code so ändern, dass die Funktion wieder einwandfrei arbeitet?

    • c

      Ist mir noch nicht aufgefallen, weil ich solche Hinweise nicht aktiviert habe. :) Und funktionieren tut’s ja noch. (Noch…)
      So wie’s aussieht, soll man $wpdb->escape durch esc_sql (ohne $wpdb) ersetzen. Scheint bei einer schnellen Änderung hier auch zu gehen.

  6. R

    Wie blöd, darauf, dass $wpdb wegzulassen, hätte ich auch selber kommen können. Vielen Dank für den Support! Jetzt funktioniert es wieder einwandfrei und produziert keine Fehlermeldung mehr :)

  7. R

    Ich schon wieder. Immer noch. Bin sozusagen ein treuer Kunde.

    Habe noch eine Frage zum Plugin. Momentan scheint es Kommentare via dem Namen zu zählen, wenn also jemand „Bernd“ heißt und das erste Kommentar abgibt, könnte ein anderer „Bernd“ kommen und der Code würde um 1 hochzählen. Richtig? So ist es jedenfalls bei mir zur Zeit. Gibt es eine Möglichkeit die Zählmethode so zu ändern, dass zum beispiel anhand der E-Mail Adresse oder einer Kombination aus beidem zu zählen um die Angaben dann verlässlicher zu machen?

    Es wäre schön, wenn du noch einmal helfen würdest :)

    • c

      Richtig. Hab ich so gemacht, weil’s immer wieder Leute gab, die ihre Mail-Adresse oder gleich ihr Blog geändert hatten.

      Wenn du „GROUP BY comment_author“ durch „GROUP BY comment_author, comment_author_email“ ersetzt, wird die Adresse auch zur Unterscheidung herangezogen.

Schreib einen Kommentar

Alle Angaben sind freiwillig. Die E-Mail-Adresse wird nicht veröffentlicht oder weitergegeben.

  • Moderation: Wer zum ersten Mal kommentiert, dessen Kommentar muss manuell von mir freigeschaltet werden.
  • Benimm dich! Keine Beleidigungen, keine rechtswidrigen Inhalte u.s.w.! Sollte eigentlich selbst­verständlich sein, oder...?
  • Webseite: Nichts gegen Blogs mit Werbung, aber rein kommerzielle Links sind unerwünscht und werden gelöscht. Reine Spam-Kommentare natürlich auch.
  • Erlaubte HTML-Tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <sub> <sup> <big> <small> <u>