Der TYPO3-Standard-Wrap kann mehr, als man denkt: stdWrap verstehen und anwenden
stdWrap, der TypoScript-Standard-Wrap, ist eine der am häufigsten verwendeten TypoScript-Funktionen. Die bekannteste Anwendung der stdWrap-Funktion ist das TypoScript-Objekt „TEXT“:
page.98 = TEXT # in allen hier abgebildeten Beispielen ist page.98 immer ein TEXT page.98 { value = Startseite typolink.parameter = 123 wrap = <div class=“home“>|</div> }
Listing 1
Schaut man sich nun an, was hier tatsächlich passiert, so findet man im TYPO3-Sourcecode in der Datei
„typo3\sysext\cms\tslib\class.tslib_content.php“ folgenden Aufruf:
function TEXT($conf) { return $this->stdWrap($conf['value'],$conf); }
Listing 2
Es wird also eine tatsächliche PHP-Funktion aufgerufen, inklusive aller Parameter, die man dem Objekt „TEXT“ im TypoScript zugewiesen hat. Diese stellt sich – stark gekürzt – wie folgt dar:
function stdWrap($content,$conf) { if (is_array($conf)) { if ($conf['field']) { $content=$this->getFieldVal($conf['field']); } if ($conf['stdWrap.']){ $content=$this->stdWrap($content,$conf['stdWrap.']); } if ($conf['typolink.']){ $content=$this->typolink($content, $conf['typolink.']); } if ($conf['wrap']){ $content=$this->wrap($content, $conf['wrap']); } } return $content; }
Listing 3
stdWrap ist demnach eine Funktion, die andere Funktionen aufruft, sofern man diese konfiguriert hat. Sie bekommt einen String namens $content als Eingabe, bearbeitet diesen und gibt ihn wieder aus.
Man kann mehrere Funktionen in einem Schritt aufrufen. Sie werden dann immer in der Reihenfolge ausgeführt, die im Quellcode definiert wurde. Die Konfigurationsreihenfolge im TypoScript spielt dabei keine Rolle.
Anwendungsgebiete von stdWrap
stdWrap kann man nur dort nutzen, wo diese Funktion auch definiert ist. Letzten Endes ist stdWrap nur eine PHP-Funktion, die auch aufgerufen werden muss. Wann immer man stdWrap auf einen Parameter anwenden kann, so steht bei diesem in der TSref etwas in der Form „int+ / stdWrap“ oder „string /stdWrap“.
Manche – aber nicht alle – cObjects kann man auch komplett per stdWrap wrappen. Sie haben dann meist einen Parameter namens „stdWrap“. Ein guter Trick für Elemente, die sich nicht ohne Weiteres wrappen lassen, ist, diese einfach in das Array „COA“ zu packen:
page.10 = COA page.10.10 < plugin.tx_myplugin page.10.stdWrap.ifEmpty = Plugin ist leer.
Listing 4
Wenn man bei einem Parameter keine stdWrap-Eigenschaft definiert hat, kann man diese leider auch nicht nutzen.
Die unterschiedlichen stdWrap-Funktionen
Einen Überblick über die verfügbaren stdWrap-Funktionen bekommt man im TSref [1] und im offiziellen TYPO3-Wiki [2].
Die Funktionen werden in der Reihenfolge, in der sie auch im Wiki stehen, abgearbeitet. Dabei ist die Reihenfolge, in der das TypoScript definiert wurde, unerheblich.
Die stdWrap-Funktionen kann man grob in drei Gruppen unterteilen, die nacheinander in dieser Reihenfolge aufgerufen werden:
- Daten holen, zum Beispiel „field“ oder „data“
- Daten überprüfen/überschreiben, beispielsweise „ifEmpty“
- Daten verarbeiten, zum Beispiel „crop“, „typolink“, „wrap“
Daten holen
#Feld subtitle holen page.98.field = subtitle #herausfinden, welche Felder es gibt page.98.debugData = 1
Listing 5
Die wohl bekannteste Funktion zum Holen von Daten ist „field“. Mit dieser kann man ein aktuell verfügbares Feld auslesen. Das sind normalerweise die Felder auf der Seite, auf der man sich gerade befindet. Innerhalb eines Menüs wäre es die aktuelle Seite im Menü, innerhalb einer Extension könnten es beispielsweise Felder des anzuzeigenden Datensatzes sein. In der Regel kann „field“ auf alle Felder einer Datenbanktabelle zugreifen. In einigen Extensions werden die Felder aber auch gefiltert oder neue hinzudefiniert. Welche Felder aktuell verfügbar sind, kann man sich anzeigen lassen, indem man die letzte Zeile in Listing 5 testweise setzt.
#Mit data kann man unter anderem folgendes auslesen: #Aktuelle Felder page.98.data = field:subtitle #Felder anderer Tabellen page.99.data = {DB:static_countries:56:cn_iso_2} #GET- und POST Variable: page.100.data = GP:tx_ttnews|tt_news # GPVar is seit Version 4.3.x deprecated #Cookies: page.101.data = global : HTTP_COOKIE_VARS | mycookie #Cookies: page.102.data = global : HTTP_COOKIE_VARS | mycookie #Lokalisierte Texte: page.103.data = LLL:EXT:tt_news/pi/locallang.xml:latestHeader #Register: page.104.data = register:myCount #Und vieles mehr.
Listing 6
Wer schon einmal versucht hat, in der TSref zu verstehen, was „getText“ eigentlich macht, der wird in der Regel verwirrt gewesen sein. Dort steht nämlich nur, dass „data“ vom Typ „getText“ ist. Im TYPO3-Wiki ist „getText“ zum Glück detaillierter beschrieben [3].
lib.myext.title.stdWrap.cObject = IMAGE lib.myext.title.stdWrap.cObject.file = GIFBUILDER
Listing 7
Mit „cObject“ kann man die Ausgabe eines anderen Objekts als Grundlage nutzen. Im Zusammenhang mit „TEXT“ weniger einleuchtend ist die Möglichkeit, die gesamte Ausgabe der stdWrap-Funktion nach einem anderen Element umzubiegen. Möchte man aber zum Beispiel einen Titel durch ein GIFBUILDER-Objekt ersetzen, um ihn grafisch darzustellen, kann das sehr sinnvoll sein:
Daten überschreiben
# Output: angezeigt! page.98 .value = gelöscht? page.98.override = angezeigt! #Output: gelöscht? page.98 .value = gelöscht? page.98.ifEmpty = angezeigt!
Listing 8
Die drei Funktionen „ifEmpty“, „ifBlank“ und „override“ überschreiben Daten und scheinen ähnlich zu wirken, unterscheiden sich aber dennoch: Sollte nach dem Holen der Daten aus dem vorherigen Schritt der Inhalt des Links leer sein, so wird dieser mit dem Inhalt von „ifEmpty“ oder „ifBlank“ ersetzt. Dabei interpretiert „ifEmpty“ den leeren String “, die Zahl 0 sowie den String ‚0‘ als leer. „ifBlank“ interpretiert nur den leeren String “ als leer.
„override“ dagegen überschreibt in jedem Fall den Wert, den „$content“ in dem Moment hat, es sei denn, der Inhalt von „override“ selbst ist leer oder ‚0‘.
/* Manchmal ist auch ein „Notausstieg“ sinnvoll. Diese Funktionen geben sofort den leeren String, wenn */ # der derzeitige Inhalt leer oder 0 ist: page.98.required = 1 # das Feld media gesetzt ist: page.98.fieldRequired = media # das Feld media ist nicht gesetzt page.98.if.isFalse.field = media # die Seite den Typ „normale Seite“ hat: page.98.if.equals.value.field = doktype page.98.if.equals = 1
Listing 9
Die Funktion „trim“ wird nach „required“ aber vor dem „if“ aufgerufen. Das hat praktische Gründe, thematisch würde sie besser zur Gruppe „Daten verarbeiten“ passen.
Daten verarbeiten
Die größte Gruppe der stdWrap-Funktionen ist jene zum Verarbeiten der Daten. Viele trivialere stdWrap-Funktionen stellen einfach nur entsprechende PHP-Funktionen zur Bearbeitung von Strings zur Verfügung.
Dazu zählen zum Beispiel: „intval“, „stripHtml“, „htmlSpecialChars“, „case = upper“ und weitere. Andere Funktionen rufen entsprechende TYPO3-API-Funktionen auf.
Da es unmöglich ist, in diesem Rahmen alle stdWrap-Funktionen zu beschreiben, werden im Folgenden nur einige wichtige vorgestellt, die nach Themen geordnet sind.
function wrap($content,$wrap,$char='|') { if ($wrap) { $wrapArr = explode($char, $wrap); return trim($wrapArr[0]).$content.trim($wrapArr[1]); } else return $content; }
Listing 10
Es gibt insgesamt fünf normale Wraps. Diese werden in der Reihenfolge „innerWrap“, „innerWrap 2“, „wrap“, „wrap2 [,3]“, „outerWrap“ aufgerufen. In der Regel wird bei ihnen die Pipe „|“ durch den aktuellen Inhalt von „$content“ ersetzt.
page.98.value = I need my pipes page.98.wrap = |||*||| page.98.wrap.splitChar = *
Listing 11
Bei „wrap“, „wrap2“ und „wrap3“ kann man zur Aufspaltung anstelle der Pipe „|“ auch ein anderes Zeichen definieren.
page.98.value = Ein Text page.98.innerWrap.data = LLL:EXT:myext/locallang.xml:wrapMe
Listing 12
Beim „inner-“ und „outerWrap“ ist das allerdings nicht möglich. Dagegen kann bei „innerWrap“, „innerWrap2“ und „outerWrap“ der Text des Wraps selbst nochmal durch die stdWrap-Funktion gejagt werden. Das wiederum ist bei „wrap“ bis „wrap 3“ nicht möglich, was vermutlich historische Gründe hat.
page.98. noTrimWrap = |Ich | Dich| page.99. dataWrap = <div class=“{field:myClass}“>|</div>
Listing 13
„noTrimWrap“ funktioniert wie der normale Wrap, entfernt jedoch keine Leerzeichen. Um Anfang und Ende zu definieren, muss der Wrap in zusätzliche Pipes eingepackt werden.
Beim „dataWrap“ wird sämtlicher Text, der zwischen geschwungenen Klammern liegt, durch die „getText“-Funktion gejagt. Also in etwa so, als hätte man ihn bei Data eingegeben.
Zu den wichtigsten HTML-Funktionen gehören „HTMLparser“, „stripHtml“ und „br“. Die Funktion „br = 1“ ist zwar simpel, aber oft sehr hilfreich. Ist sie gesetzt, so werden alle im Text vorhandenen New-Lines durch „<br />“-Tags ersetzt – so kann man Zeilenumbrüche im Frontend ausgeben.
„stripHtml“ entfernt sämtliche HTML-Tags. Dies ist wichtig, wenn man einen Text zum Beispiel (per crop) kürzen will.
Der HTMLParser kann HTML-Eingaben parsen, verbotenes HTML entfernen oder nur erlaubte Tags darin belassen. Verwandt, aber noch komplexer ist die „parseFunc“, die auch vom RTE genutzt wird. Seit TYPO3 4.3 gibt es die Funktion „cropHTML“. Diese kürzt Texte und bewahrt dabei alle HTML-Tags.
stdWrap rekombinieren
Eine große Stärke von stdWrap liegt darin, dass man die einzelnen Funktionen beliebig kombinieren kann. Dabei gibt es zwei mögliche Kombinationsformen: die lineare Kombination und die rekursive Kombination.
# Ausgabe: stdWrap ist prima page.98.value = stdWrap ist toll page.98.crop = 7 page.98.noTrimWrap = || ist prima|
Listing 14
Bei der linearen Kombination werden die Funktionen einfach hintereinander aufgerufen und zwar in der Reihenfolge, die im Quellcode definiert war.
# Ausgabe: stdWrap ist prima page.98.value = stdWrap ist toll page.98.stdWrap.crop = 7 page.98.noTrimWrap = || ist prima| # Ausgabe: stdWrap page.98.value = stdWrap ist toll page.98.crop = 7 page.98.stdWrap.noTrimWrap = || ist prima|
Listing 15
Beim rekursiven Aufruf dagegen wird die stdWrap-Funktion noch ein weiteres Mal aufgerufen. Dadurch kann man die Ausführungsreihenfolge der Funktionen beeinflussen:
Wenn man die Aufrufreihenfolge der Funktionen nicht ändern muss, sollte man der linearen Kombination den Vorzug geben – diese ist performanter.
Komplexe stdWrap-Beispiele
lib.myNewsTitle = TEXT lib.myNewsTitle.data = DB:tt_news:188:title
Listing 16
Kennt man die UID eines Newsdatensatzes, so kann man dessen Titel wie oben beschrieben einfach auslesen.
Was aber, wenn man die UID des tt_news-Datensatzes aus einer GET-Variable auslesen muss, um zum Beispiel den Titel der aktuell anzuzeigenden News in der SINGLE-Ansicht auszulesen? Das geht unter anderem so:
1 lib.myNewsTitle = TEXT 2 lib.myNewsTitle { 3 dataWrap = DB:tt_news:{GP:tx_ttnews|tt_news}:title 4 wrap3 = {|} 5 insertData = 1 6 }
Listing 17
Aber wie funktioniert das? Die Funktionen werden in der Reihenfolge aufgerufen, in der sie im Beispiel stehen. Zunächst wird also in Zeile 3 der dataWrap ausgeführt. Danach hat „$content“ den Wert „DB:tt_news:188:title“. Durch wrap3 in Zeile 4 wird „$content“ umgewandelt in „{DB:tt_news:188:title}“. insertData in Zeile 5 parst nun noch einmal den String nach geschwungenen Klammern und schickt den Inhalt durch die getText-Funktion. Danach hat $content als Wert den Titel der News – dieser wird auch zurückgegeben.
# Pipes in Zeilenumbrüche verwandeln page.98 { value =Jochen|Kasper|Robert split { token = | wrap = <br /> } } /* Ausgabe: Jochen<br /> Kasper<br /> Robert <br /> */ # Bestimmten Datensatz zurückgeben page.99 { value = Jochen|Kasper|Robert split { token = | wrap = <br /> returnKey = 1 } } #Ausgabe: Kasper
Listing 18
Die Split-Funktion gehört zu den komplizierteren stdWrap-Funktionen. Wenn man sich mit ihr intensiver auseinandersetzt, merkt man schnell, wie mächtig sie ist. Es gibt ein sehr gutes Tutorial dazu im Wiki [4], daher oben zwei kleine Beispiele zur Motivation.
stdWrap erweitern
Wem die eingebauten stdWrap-Funktionen nicht genügen, der kann entweder eine der zwei integrierten userFuncs „preUserFunc“ und „postUserFunc“ oder einen der derzeit vier vorhandenen Hooks nutzen. Nähere Infos dazu finden sich auch in dem Handout zu einem stdWrap-Vortrag der Autorin [5].
Die Zukunft von stdWrap
Auf den TYPO3 Developer Days 2010 in Elmshorn wurde im Rahmen eines Workshops die nahe Zukunft von stdWrap und TypoScript besprochen. Die Neuerungen werden voraussichtlich in TYPO3 4.5 zur Verfügung stehen. Das Projekt dazu heißt „Pimp Your TypoScript“ [6]. Neben Verbesserungen der Code-Qualität hinsichtlich Performance und Erweiterbarkeit soll es auch für Entwickler einige nützliche Neuerungen geben.
So wird es künftig möglich sein, an jeder Stelle des TypoScripts stdWrap-Funktionen anzuwenden. Ausnahmen gibt es nur bei Kollisionen mit bestehenden Funktionsnamen, etwa „crop“ beim GIDBUILDER. Dieses neue Feature wird „chained TypoScript“ genannt. Unter anderem wird es damit möglich, die Konfiguration einer stdWrap-Funktion durch TypoScript zu definieren.
# TYPO3 v. 4.4 page.98.crop = 255|..|1 # ab v. 4.5 page.98.crop.field = cropAt
Listing 19
Ab Version 4.5 soll es zudem möglich sein, per stdWrap-Funktion zu ermitteln, um wie viel hier nun gecropt werden soll.
Das Beispiel mit dem auslesen des Newstitels hatte ich auch hier schon mal ausführlicher erklärt: Datenbankfelder dynamisch per stdWrap.data auslesen
LG Lina