Schlagwörter von privaten Beiträgen in der Tag-Cloud anzeigen

Keine Angst, an den letzten Wochenenden gab es mal kein WordCamp und daher gibt es heute mal wieder einen normalen Beitrag 😉 Vorletzte Woche gab es an einem Kundenprojekt eine interessante Anfrage. Die WordPress Website hatte einen FAQ-Bereich und manche Fragen waren auf privat gesetzt. Die Website hat außerdeme eine Tag-Cloud eingesetzt, um es den Besuchern zu erleichtern, passende Fragen zu finden. Aber selbst, wenn sich die Nutzer angemeldet haben, konnten sie keine Schlagwörter in der Tag-Cloud sehen, die lediglich in privaten Fragen verwendet wurden.

Wie werden die Schlagwörter in der Tag-Cloud erzeugt?

Wenn in WordPress der HTML-Code für die Schlagwörter-Wolke erzeugt wird, dann werden nur eine begrenzte Anzahl an Schlagwörtern ausgelesen. Aber wie genau wird diese Auswahl berechnet? Man würde vermuten, dass hierzu eine Datenbank-Abfrage gestartet wird, die nach der Häufigkeit des verwendeten Schlagwortes gruppiert und die IDs zurückliefert, die am meisten verwendet wurden. Aber so funktioniert es nicht in WordPress. Aus Geschwindigkeitsgründen wird die Anzahl der Nutzung eines Schlagwortes statisch in einer Spalte in der Datenbanktabelle gespeichert. Aber wieso sind dann private Beiträge in der Tag-Cloud nicht sichtbar? Das liegt daran, dass sobald man die Sichtbarkeit eines Beitrags auf privat stellt, dieser Beitrag nicht mehr mitgezählt wird, wenn es um die Ermittlung der Gesamtanzahl geht. Nur öffentliche Beiträge werden hierbei beachtet. Es gibt also leider keinen einfachen Lösungsweg über einen Filter oder eine Action, um auch private Beiträge anzuzeigen. Aber wie können wir das Problem lösen?

Die Datenbankabfrage anpassen

Fadst jede Datenbankabfrage, die “eine Liste” erzeugt wird in WordPress durch die Klasse WP_Query umgesetzt. Für die Tag-Cloud ist es genau genommen die Klasse WP_Term_Query, die aber sehr ähnlich funktioniert. Wir müssen also lediglich die Abfrage ändern, die hierbei ausgeführt wird. Aber wie stellen wir fest, dass es sich bei der aktuellen Abfrage um die Tag-Cloud handelt und nicht im eine andere Abfrage auf Taxonomien? Es gibt leider keinen Conditional-Tag is_tag_cloud() oder ähnliches.

Im Grunde ist es recht einfach. Bei der Erstellung der Schlagwörter-Wolke werden zwei Argumente eingesetzt, die nur hier vorkommen: largest und smallest. Diese beiden Werte geben an, welches die größte bzw. kleinste Schriftgröße ist, die in der Tag-Cloud verwendet werden soll. Wir können also einfach prüfen, ob einer dieser beiden Argumente vorhanden ist und wissen dann, dass es sich bei der aktuellen WP_Term_Query um die Abfrage für die Tag-Cloud handelt. Der Rest ist eigentlich recht einfach. Wir müssen nur den term_clause Filter verwenden, um die SQL-Abfrage entsprechend anzupassen:

function private_posts_in_tc_terms_clauses( $pieces, $taxonomies, $args ) {
	if ( isset( $args['largest'] ) && is_user_logged_in() ) {
		// Count by the grouped term_id.
		$pieces['fields'] .= ', COUNT(t.term_id) AS grouped_count';
		// Join the relationship to the posts.
		$pieces['join'] .= ' INNER JOIN wp_term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id';
		// Remove the "redundant" count (only for public posts).
		$pieces['where'] = str_replace( 'AND tt.count > 0', '', $pieces['where'] );
		// Group by the term_id to remove duplicates and calculate the count.
		$pieces['where'] .= ' GROUP BY tt.term_id HAVING grouped_count > 0';
	}
	
	return $pieces;
}
add_filter( 'terms_clauses', 'private_posts_in_tc_terms_clauses', 10, 3 );

Bevor wir die Abfrage anpassen, müssen wir natürlich noch sicherstellen, dass der aktuelle Nutzer auch angemeldet ist und die privaten Schlagwörter auch sehen darf. In der veränderten Abfrage machen wir dann einen JOIN auf die term_relationship Tabelle und gruppieren anhand der Term-ID. Hierdurch können wir dann mit SQL sehr einfach die Anzahl inklusive der privaten Beiträge berechnen. Die Abfrage selbst ist natürlich etwas langsamer als die Originalabfrage, da sie keine zwischengespeicherten Werte aus der Tabelle verwendet. Aber da ohnehin jede WP_Term_Query in einem Transient zwischengespeichert wird, ist die Performance-Einbuße nicht wirklicht groß.

Fazit

Es ist nicht immer ganz so einfach, dass man nur den richtigen Hook finden muss, um eine Query in WordPress zu ändern. Aber normalerweise gibt es fast immer einen Weg, das gewünschte Ergebnis zu erreichen. Wenn ihr diese Funktion auch mal ausprobieren wollt, dann findet ihr den Code dazu wie immer in einem GIST. Hier könnt ihr euch auch wieder alles als ZIP-Datei runterladen und einfach als Plugin auf eurer Seite installieren.

Veröffentlicht von

Bernhard ist fest angestellter Webentwickler, entwickelt in seiner Freizeit Plugin, schreibt in seinem Blog über WordPress und andere Themen, treibt sich gerne bei den WP Meetups in Berlin und Potsdam herum und läuft nach Feierabend den ein oder anderen Halbmarathon.

2 Kommentare » Schreibe einen Kommentar

  1. Was mich interessieren würde, ist, wie man das hier löst:
    “hatte einen FAQ-Bereich und manche Fragen waren auf privat gesetzt”
    Hättest du einen Hinweis/Link/Tutorial bitte, wo ich das nachvollziehen/-bauen könnte, ohne jetzt so wahnsinnig fit im Coden zu sein 😉 ?

    • Hallo Chris,

      ich versuche mal, die zu beschreiben, wie ich das umgesetzt habe. Die FAQ sind ein Custom Post Type (CPT). Der Titel ist hierbei die Frage, der Inhalt ist die Antwort. Weiterhin gibt es zwei Custom-Taxonomies (CT) “FAQ-Kategorien” und “FAQ-Schlagwörter”. Spezielle Felder haben weder der Post-Type, noch die Taxonomies.

      Es gibt viele Anleitungen zum Thema CPT und CT. Man kann diese auch mit Plugins im Backend erstellen. Eine Empfehlung zu solchen Plugins kann ich dir leider nicht geben, da ich diese nicht verwende.

      Es sollte aber auch möglich sein, das ganze mit Beiträgen umzusetzen, die in einer Hauptkategorie “FAQ” einsortiert sind. Der Nachteil hiervon wäre aber, dass diese auch in der Blogübersicht auftauchen.

      Ich hoffe mal, das konnte dir eine Idee davon geben, wie ich es gelöst habe.

Schreibe einen Kommentar

Pflichtfelder sind mit * markiert.