Für ein Kundenprojekt auf einer WordPress-Multisite und mit einer sehr großen Anzahl an Einträgen, die aus einer Warenwirtschaft gespeißt werden, sollten im Frontend bestimmte Post-Metadaten formatiert werden ohne das bei jeder einzelnen Ausgabe im Frontend über das richtige Format nachgedacht werden muss.
Zum Beispiel können Entfernungen oder Gewichte mit ihren Einheiten versehen werden.
Datenbank: „5“
Frontend: „5 kg“ oder „5,00 kg“
Das Problem
Um einen eigenen Formatierungs-Helfer zu vermeiden war meine Idee, dann filtern wir doch einfach den Wert unmittelbar bevor er von der get_post_meta() Funktion zurück gegeben wird. Pech gehabt. In der Funktion und allen weiteren Funktionen die sie aufruft gibt es nur einen relevanten Filter: get_{$meta_type}_metadata
Dieser dient eigentlich dazu zu prüfen ob der aktuelle Benutzer einen Postmeta Wert überhaupt auslesen darf und im negativen Fall eine Alternative zurück zu liefern.
In dem Moment wo diese Prüfung ausgeführt wird „kennt“ das Programm den Postmeta-Wert noch nicht. Ist somit nicht Teil der Parameter von apply_filters().
Fügt man einen eigenen Filter an der Stelle hinzu kann man wierderum get_post_meta() aufrufen. Aber dann landed man direkt wieder in dem selben eigenen Filter und somit in einer Endlosschleife.
Damit dachte ich erst wäre meine Idee gescheitert. Doch hatte ich zufällig die current_filter() Funktion beim debuggen verwendet und kam auf die Idee, ob ich nicht herausfinden könnte ob mein Filter gerade sich selber filtert.
Nicht schön aber machbar die Globale $wp_current_filter gedumpt und da war er der Filter im Filter.
Die Lösung
Im folgenden Gist habe ich den Code beispielhaft Dargestellt:
Zu nächst wende ich den Filter nur an wenn wir uns im Frontend bewegen.
Aus dem Baum aller Filter und Hooks in denen ich mich gerade befinde hole ich den betroffenen Array-Key raus und schaue ob der Filter sich gerade selber aufruft.
Wenn dies der Fall ist halte ich mich aus dem Prozess raus in dem ich das NULL in der $input Variablen zurückgebe.
Somit kann WordPress den Wert aus dem Cache oder der DB holen.
Wenn die Prüfung feststellt, dass dies der „erste Aufruf“ des Filters ist wird geschaut ob gerade der zu formatierende Metawert angefordert wird.
Wird der ‚distance‘ Wert angefordert wird dieser per get_post_meta() geholt. Dabei ist es egal ob man die Funktion oder die magische Methode $post->distance verwendet. Der Aufruf landet immer in der obigen Prüfung und löst dort den if-Block aus.
Fazit
Eine Funktion innerhalb der selben Funktion aufzurufen und dann noch darin herum zu filtern ist nicht besonders gerade aus aber für meinen Anwendungsfall klappt es super.
Die Entwickler im Projekt, die die Frontends bauen benötigen nur get_post_meta() aufrufen und sich nie sorgen um die Formatierung machen. Und ich habe die Formatierung im gesammten Projekt im Grif. Wenn nötig passe ich die Formatierung an einer Codestelle an und Tausende von Darstellungen ändern sich.
Stefan Kremer
Bin mir nicht ganz sicher, ob ich alles richtig mitbekommen habe oder ob du’s geschafft hast mir einen Knoten in den Bregen zu setzen. Wie schaut das in Sachen Performance aus? Gerade weil du von großen Datenmengen sprachst.
Drivingralle
Die Prüfungen innerhalb des Filters sind sehr simple und nur in den Fällen wo ich formatieren will wird eine extra Schleife gedreht. Daher sollte sich das nicht merkbar auf die Performance auswirken.
Caspar
Cool. Kannst du noch kurz ein Beispiel für den Anwendungsfall nennen? Ich stelle mir zum Beispiel einen Produktpreis vor, der im Frontend immer in ein
<strong>
gewickelt werden soll. Verstehe ich das Prinzip richtig?Drivingralle
Formatierungen kann man damit auch machen.
Haben das eher für das anfügen von Einheiten verwendet. Beispiel im Test oben ergänzt.
Caspar
Klasse, danke! 👌