Fluid ViewHelper Dokumentation

Warum das Ganze?

Ich bekomme immer wieder Mails, dass es im Internet keine vernünftige Dokumentation zu Fluid gibt. Viele haben immer wieder Probleme damit Übersetzungen mit HTML-Tags auf die Webseite zu transportieren. Die Suche ging in die Stunden. Für uns Programmierer kein Thema: Wir gehen in den Quellcode der Fluid-ViewHelper und ziehen uns die benötigten Daten direkt aus dem Quellcode. Viele ziehen sich die Informationen auch aus dem CheatSheet von Patrick Lobacher sofern sie denn überhaupt wissen was htmlEscape und Kollegen machen. In den Büchern wird gerade mal mit nem Satz oder Zwei auf einen Parameter eingegangen. Wenn überhaupt. Hinzu kommt, dass gefühlte 20 neue ViewHelper in der 4.6er Version hinzugekommen sind. Die sind in den Büchern noch gar nicht dokumentiert. Auch die Inlinenotation  ist nur geringfügig erklärt und Beispiele gibt es nur in den Kommentaren des ViewHelper-Quelltextes.

Kein Wunder, dass sich die Postfächer mit Fragen füllen. Ich habe heute (14.04.2012) damit angefangen eine öffentliche ViewHelper-Dokumentation zu schreiben und kann nur hoffen, dass die Fragen bald über Google gefunden werden und die Postfächer überschaubar bleiben.

Um konstruktive Kritik/Lob/Fehler wird gebeten.

f:alias

Parameter

  • map: Die Eingabe erfolgt in Arraynotation. Der Key/Schlüssel gibt den Namen der neuen Variable wieder während der Wert den Inhalt wieder spiegelt.

Mit diesem ViewHelper könnt Ihr eigene Variablen innerhalb des öffnenden und schließenden Tags zur Verfügung stellen. Das ist sinnvoll, wenn Ihr einen bestimmten Wert (Texte, Zahlen oder Werte aus Objekten) in Eurem Template mehrfach benötigt

Beispiel für einfache Texte

 

<f:alias map="{vorname: 'Stefan', nachname: 'Froemken'}">
<p>Hallo zusammen,<br />mein Name ist {vorname} {nachname}</p>
</f:alias>

 

Beispiel für selbst erstellte Arrays

Dieses Beispiel ist vielleicht wenig sinnvoll, aber es zeigt sehr gut, wie man innerhalb von Fluid mehrdimensionale Arrays erstellen kann.

 

<f:alias map="{company: {name: 'Mueller und Co.', mitarbeiter: {0: {name: 'Stefan'}, 1: {name: 'Petra'}}}}">
<p>Wir, die Firma {company.name}, haben folgende Mitarbeiter:</p>
<ul>
<f:for each="{company.mitarbeiter}" as="mitarbeiter">
<li>{mitarbeiter.name}</li>
</f:for>
</ul>
</f:alias>

 

Beispiel für Werte aus Objekten

Dieses Beispiel funktioniert nur, wenn Ihr das feUser-Objekt in Eurer Extension per $this->view->assign() verfügbar gemacht habt.

 

<f:alias map="{vorname: feUser.firstName, nachname: feUser.lastName}">
<p>Hallo zusammen,<br />mein Name ist {vorname} {nachname}</p>
</f:alias>

f:base

Dieser ViewHelper macht nur dann Sinn, wenn Ihr Eure komplette Seite/Template (inkl. Headerbereich) mit Hilfe von Fluid aufbaut. Ansonsten würde dieser Tag immer im Bodybereich der Webseite erstellt, wo er keinen Sinn macht bzw. nicht hingehört.

Beispiel

Es gibt keine Parameter für diesen ViewHelper, daher bleibt es bei diesem kurzen Beispiel.

 

<f:base />

f:cObject

Parameter

  • typoscriptObjectPath (Pflichtangabe): Gebt hier den TypoScript Objektpfad an wie z.B. lib.beispieldaten.10
  • data: Hiermit könnt Ihr dem TypoScript Objektpfad noch ein paar Daten mit auf den Weg geben. Handelt es sich dabei um ein Objekt oder ein Array, dann könnt Ihr mit Hilfe von "field" auf die Werte der Keys/Schlüssel zugreifen. Handelt es sich um einen Text oder eine Zahl, dann wird der Wert über "current" verfügbar gemacht.
  • currentValueKey: Wenn im Bereich "data" mit Arrays oder Objekte gearbeitet wird, dann kann man mit diesem Parameter angeben, welcher Wert eines Schlüssels über "current" verfügbar gemacht werden soll.

Das ist in meinen Augen ein sehr mächtiger ViewHelper, da Ihr hiermit die geballte Ladung von TypoScript zur Verfügung habt. Nähere Erläuterungen in den Beispielen:

Einfaches Beispiel

im TypoScript (das statische Template von css_styled_content muss eingebunden sein):

 

lib.content < styles.content.get

 

Im Fluidtemplate dann

 

<f:cObject typoscriptObjectPath="lib.content" />

 

Mit diesem einfachen Beispiel werden alle tt_content-Datensätze der aktuellen Seite ausgegeben

Beispiel mit Text zwischen den Tags

Im TypoScript:

 

lib.content = TEXT
lib.content {
  value.current = 1
  value.wrap = <p>Das ist der Text zwischen den Tags<br />|</p>
}

 

im Fluidtemplate

 

<f:cObject typoscriptObjectPath="lib.content">
    Stefan heisst Euch willkommen
</f:cObject>

 

oder auch:

 

<f:cObject typoscriptObjectPath="lib.content" data="Stefan heisst Euch willkommen" />

 

Der ViewHelper erkennt, dass Ihr nur Text eingetragen habt. In diesem Falle wird dieser mit Hilfe von "current = 1" im TS zur Verfügung gestellt.

Beispiel mit einem Array/Objekt

Falls kein Array oder Objekt zur Hand, dann machen wir uns eins:

 

<f:cObject typoscriptObjectPath="lib.content" data="{vorname: 'Stefan', nachname: 'Froemken'}" currentValueKey="nachname" />

 

Wenn Ihr ein Objekt oder Array habt, dann reicht natürlich diese Form:

 

<f:cObject typoscriptObjectPath="lib.content" data="{feUser}" currentValueKey="lastName" />

 

Das TS könnte dann so aussehen:

 

lib.content = COA
lib.content {
  10 = TEXT
  10.value.current = 1
  10.value.wrap = <p>Nachname: |</p>
  20 = TEXT
  20.value.dataWrap = Vollständiger Name: {field: vorname} {field: nachname}
}

 

Wie oben schon erwähnt könnt Ihr mit currentValueKey angeben, welcher Wert aus dem Array oder Objekt über current=1 zur Verfügung gestellt werden soll. Auf die anderen Werte eines Arrays oder Objektes könnt Ihr dann wie hier im Beispiel mit "field" zugreifen.

f:comment

Für diesen ViewHelper gibt es keinen Parameter. Alles was sich innerhalb der Tags befindet, wird schlichtweg entfernt. Andere enthaltene ViewHelper werden auch nicht ausgeführt. Dieser ViewHelper ist schon mal sinnvoll zum Debuggen der Webseite. So kann man z.B. f:for-Schleifen damit temporär ausblenden, um Fehlersuchen auf das Wesentliche zu beschränken.

Beispiel

 

<f:alias map="{vorname: 'Stefan'}">
    <f:comment>
        <p>Dieser Text wird nicht angezeigt</p>
        <p>Mein Name ist: {vorname}</p>
    </f:comment>
    <p>{vorname} wohnt in Lindlar</p>
    <![CDATA[<p>{vorname} wohnt in Lindlar</p>]]>
</f:alias>

 

Alles zwischen den f:comment-Tags wird überhaupt nicht gerendert, angezeigt oder überhaupt auch nur verarbeitet. Die Sache mit "Stefan wohnt in Lindlar" wird ganz normal angezeigt. Die letzte Zeile wird allerdings nochmal interessant. Denn mit Hilfe dieser CDATA-Notation könnt Ihr Euch den original Quelltext wie im Fluidtemplate anzeigen lassen.

f:count

Parameter

  • subject: Gebt hier das Array oder Objekt an, das gezählt werden soll. Wenn dieser Wert leergelassen wird, wird versucht, den Inhalt zwischen den Tags zu zählen

Dieser ViewHelper zählt Arrayelemente oder Inhalte in einem Objekt

Beispiel mit Inhalt zwischen den Tags

 

<f:alias map="{mitarbeiter: {0: 'Stefan',1: 'Petra',2: 'Sascha'}}">
    <p>Bei uns arbeiten <f:count>{mitarbeiter}</f:count> Mitarbeiter</p>
</f:alias>

 

Beispiel unter Verwendung von subject

 

<f:alias map="{mitarbeiter: {0: 'Stefan',1: 'Petra',2: 'Sascha'}}">
    <p>Bei uns arbeiten <f:count subject="{mitarbeiter}" /> Mitarbeiter</p>
</f:alias>

 

Beispiel als Inlinenotation

 

<f:alias map="{mitarbeiter: {0: 'Stefan',1: 'Petra',2: 'Sascha'}}">
    <p>Bei uns arbeiten {f:count(subject: mitarbeiter)} Mitarbeiter</p>
</f:alias>

 

Beispiel als Inlinenotation extrem

 

<f:alias map="{mitarbeiter: {0: 'Stefan',1: 'Petra',2: 'Sascha'}}">
    <p>Bei uns arbeiten {mitarbeiter -> f:count()} Mitarbeiter</p>
</f:alias>

f:cycle

Parameter

  • values (Pflichtangabe): Eingabe als Arraynotation. Erläuterung im Beispiel
  • as (Pflichtangabe): Gebt hier den Namen der neuen Variable an

Sobald Ihr anfangt Fluid zur Listengenerierung zu verwenden, dann wird auch dieser ViewHelper zu Eurem Liebling. Listen werden bei Fluid mit f:for generiert. Aber diese Ausgabe ist sehr statisch. Man kann evtl. nur auf das erste und letzte Element Zugriff nehmen. Aber um jeder dritten Zeile ein anderes Aussehen zu verpassen benötigen wir diesen f:cycle ViewHelper.

Beispiel

 

<f:alias map="{mitarbeiter: {0: {vorname: 'Stefan', stadt: 'Lindlar'},1: {vorname: 'Petra', stadt: 'Lindlar'},2: {vorname: 'Sascha', stadt: 'Remscheid'},3: {vorname: 'Patrick', stadt: 'Bonn'},4: {vorname: 'Sven', stadt: 'Gummersbach'},5: {vorname: 'Andrea', stadt: 'Wuppertal'}}}">
    <table cellpadding="5" cellspacing="0" border="2">
        <f:for each="{mitarbeiter}" as="kollege">
            <f:cycle values="{0: 'green', 1: 'red', 2: 'blue'}" as="color">
                <tr>
                    <td style="color: {color}">{kollege.vorname}</td>
                    <td style="color: {color}">{kollege.stadt}</td>
                </tr>
            </f:cycle>
        </f:for>
    </table>
</f:alias>

 

Mit Hilfe von f:alias habe ich eine Variable mit 6 Einträgen erzeugt. Diese Einträge lasse ich mit f:for durchlaufen. f:cycle schaut jetzt nach, ob es sich um den ersten Eintrag handelt. Wenn ja, dann weise der Variable "color" den Wert aus dem ersten Arrayelement zu. In diesem Fall "green". Beim zweiten Durchlauf erhält die Variable "red" und zu guter Letzt "blue". Sind wir im Vierten Durchlauf, wird wieder mit dem ersten Element von f:cycle angefangen "green" und so weiter. Auf diese Weise erhalten wir eine Tabelle mit immer drei abwechselnden Farben.

f:debug

Parameter

  • title: Gibt der Debugausgabe einen Titel. Bei mehreren Debugausgaben könnt Ihr so die jeweiligen Ausgaben besser unterscheiden.

Falls eine Eurer Variablen wieder erwartend nichts ausgeben sollte, könnt Ihr mit Hilfe diesen ViewHelpers Euch komplette Arrays und unter bestimmten Voraussetzungen auch ganze Objekte anzeigen lassen. In der Ausgabe seht Ihr dann, ob Euer gewünschter Wert überhaupt gefüllt ist oder nicht.

Objekte sind teilweise so groß, dass sie einfach den Rahmen sprengen. Sie bestehen teilweise aus mehreren 10.000 Zeilen vielleicht auch aus 100.000. Auf jeden Fall sind sie teilweise so groß dass der Browser schon mal abkachelt. Bitte konvertiert in diesem Fall Eure Objekt vorher in ein Array ($result->toArray()). Dann klappts auch mit der Debugausgabe.

Beispiel

 

<f:alias map="{mitarbeiter: {0: {vorname: 'Stefan', stadt: 'Lindlar'},1: {vorname: 'Petra', stadt: 'Lindlar'},2: {vorname: 'Sascha', stadt: 'Remscheid'},3: {vorname: 'Patrick', stadt: 'Bonn'},4: {vorname: 'Sven', stadt: 'Gummersbach'},5: {vorname: 'Andrea', stadt: 'Wuppertal'}}}">
    <f:debug title="Mitarbeiter">{mitarbeiter}</f:debug>
</f:alias>

f:else

Diesen ViewHelper erkläre ich im Bereich f:if, da er nur dort verwendet werden kann.

f:escape

Veraltet: Bitte verwendet die f:format:* ViewHelper.

f:flash

Parameter

  • renderMode (Standard: ul): Sollen die Fehlermeldungen als Liste (ul) oder als Container (div) gerendert werden

Dieser ViewHelper macht nur im Bereich selbstprogrammierter Extensions Sinn. Denn nur hier können Fehler auftauchen, die dem Webseitenbesucher mitgeteilt werden müssen. Hat der User z.B. vergessen bei einem Loginformular seinen Usernamen anzugeben und die dafür zuständige Action/Methode wurde so programmiert, dass der Username eine Pflichtangabe ist, dann wird dies dem Validator gemeldet, der daraufhin eine errorAction-Methode aufruft, die dann wiederum Fehlermeldungen zuerst sammelt und dann als "Bündel" an der Stelle ausgibt, an der Ihr diesen ViewHelper-Tag platziert habt.

Beispiel Standard

 

<f:flashMessages />

 

Beispiel als Container

 

<f:flashMessages renderMode="div" />

f:for

Parameter

  • each (Pflichtangabe): Array oder Objekt, das durchlaufen werden soll
  • as (Pflichtangabe): Ein Variablenname, der Daten des aktuellen Durchlaufs enthält
  • key: Falls Ihr den Key/Schlüssel des aktuellen Durchlaufes benötigt, könnt Ihr hiermit den Namen einer weiteren Variable definieren
  • reverse: Der Durchlauf des Arrays oder Objektes geschieht rückwärts
  • iteration: Eine Arrayvariable, die Informationen darüber beinhaltet, ob man sich im ersten oder letzten Durchlauf befindet. Außerdem enthalten: index, cycle, total, isEven, isOdd

Der f:for-ViewHelper ist DER ViewHelper für die Listengeneration. Schaut Euch die Erläuterung in den Beispielen an.

Einfaches Beispiel

 

<f:alias map="{mitarbeiter: {0: {vorname: 'Stefan', stadt: 'Lindlar'},1: {vorname: 'Petra', stadt: 'Lindlar'},2: {vorname: 'Sascha', stadt: 'Remscheid'},3: {vorname: 'Patrick', stadt: 'Bonn'},4: {vorname: 'Sven', stadt: 'Gummersbach'},5: {vorname: 'Andrea', stadt: 'Wuppertal'}}}">
    <table cellpadding="5" cellspacing="0" border="2">
        <f:for each="{mitarbeiter}" as="kollege">
            <tr>
                <td>{kollege.vorname}</td>
                <td>{kollege.stadt}</td>
            </tr>
        </f:for>
    </table>
</f:alias>

 

Ich weise der Variable mitarbeiter 6 Einträge zu, die dann eins nach dem Anderen von f:for durchlaufen werden. Mit jedem Durchlauf wird eine weitere Tabellenzeile erstellt.

Beispiel für rückwärts

 

<f:alias map="{mitarbeiter: {0: {vorname: 'Stefan', stadt: 'Lindlar'},1: {vorname: 'Petra', stadt: 'Lindlar'},2: {vorname: 'Sascha', stadt: 'Remscheid'},3: {vorname: 'Patrick', stadt: 'Bonn'},4: {vorname: 'Sven', stadt: 'Gummersbach'},5: {vorname: 'Andrea', stadt: 'Wuppertal'}}}">
    <table cellpadding="5" cellspacing="0" border="2">
        <f:for each="{mitarbeiter}" as="kollege" reverse="1">
            <tr>
                <td>{kollege.vorname}</td>
                <td>{kollege.stadt}</td>
            </tr>
        </f:for>
    </table>
</f:alias>

 

Zu Info: Man könnte bei dem Parameter reverse auch TRUE statt 1 verwenden.

Beispiel mit key/Schlüssel

 

<f:alias map="{mitarbeiter: {0: {vorname: 'Stefan', stadt: 'Lindlar'},1: {vorname: 'Petra', stadt: 'Lindlar'},2: {vorname: 'Sascha', stadt: 'Remscheid'},3: {vorname: 'Patrick', stadt: 'Bonn'},4: {vorname: 'Sven', stadt: 'Gummersbach'},5: {vorname: 'Andrea', stadt: 'Wuppertal'}}}">
    <table cellpadding="5" cellspacing="0" border="2">
        <f:for each="{mitarbeiter}" as="kollege" reverse="1" key="eintrag">
            <tr>
                <th colspan="2">Eintrag: {eintrag}</th>
            </tr>
            <tr>
                <td>{kollege.vorname}</td>
                <td>{kollege.stadt}</td>
            </tr>
        </f:for>
    </table>
</f:alias>

 

Ich weiß, das ist jetzt nicht das beste Beispiel, aber Ihr seht, wie ich mit Hilfe des key-Parameters auf die keys der jeweiligen Einträge zugreifen kann.

Beispiel mit Durchlaufinformationen

 

<f:alias map="{mitarbeiter: {0: {vorname: 'Stefan', stadt: 'Lindlar'},1: {vorname: 'Petra', stadt: 'Lindlar'},2: {vorname: 'Sascha', stadt: 'Remscheid'},3: {vorname: 'Patrick', stadt: 'Bonn'},4: {vorname: 'Sven', stadt: 'Gummersbach'},5: {vorname: 'Andrea', stadt: 'Wuppertal'}}}">
<table cellpadding="5" cellspacing="0" border="2">
<f:for each="{mitarbeiter}" as="kollege" iteration="iterator">
<f:if condition="{iterator.isFirst}">
<tr><th colspan="2">Los gehts</th></tr>
</f:if>
<tr>
<td>Durchlauf beginnend bei 0: {iterator.index}</td>
<td>Durchlauf beginnend bei 1: {iterator.cycle}</td>
<td{f:if(condition:iterator.isOdd, then: ' style="color: green;"')}>{kollege.vorname}</td>
<td{f:if(condition:iterator.isEven, then: ' style="color: red;"')}>{kollege.stadt}</td>
</tr>
<f:if condition="{iterator.isLast}">
<tr><th colspan="2">Eintraege: {iterator.total}</th></tr>
</f:if>
</f:for>
</table>
</f:alias>

 

iterator.cycle ist genau wie iterator.index einfach nur ein Zähler und hat nichts mit dem ViewHelper f:cycle zu tun. Dieses Beispiel zeigt die Verwendung aller Durchlaufinformationen. Auch wenn Ihr den f:if-ViewHelper noch nicht kennengelernt haben solltet, so sollte dieses Beispiel selbsterklärend sein. Probierts mal aus.

f:form

Parameter

  • action: Welche Actionmethode soll aufgerufen werden, wenn das Formular abgesendet wird
  • arguments: Welche zusätzlichen Variablen sollen beim Absenden mit übergeben werden
  • controller (Standard: aktueller Controller): Falls sich die gewünschte Actionmethode nicht im gleichen Controller befindet, muss hier dieser entsprechende Controller angegeben werden
  • extensionName (Standard: aktuelle Extension): Falls das Formular von einer anderen Extension abgearbeitet werden soll, dann muss hier der Extensionname ohne tx_ und ohne Unterstriche angegeben werden
  • pluginName (Standard: aktuelles Plugin): Falls das Formular von einem anderen Plugin abgearbeitet werden soll, dann muss hier der Pluginname angegeben werden
  • pageUid (Standard: aktuelle Seite): Seiten-UID eintragen, wenn das Formular von einer anderen Seite aus abgearbeitet werden soll
  • object: Übergebt hier ein Objekt mit Eigenschaften, die die Eingabefelder im Formular wiederspiegeln.
  • pageType (Standard: gleicher Seitentyp): Gebt hier eine Seitentyp ID an, die das Formular abarbeiten soll. Könnte für AJAX interessant sein.
  • noCache (Standard: deaktiviert): Kann aktiviert werden, um das Caching für die Zielseite zu deaktivieren.
  • noCacheHash (Standard: deaktiviert): Nach Aktivierung wird dem generierten Link zur Zielseite kein cHash-Parameter angehangen
  • section: Definition eines Ankers zu dem auf der Zielseite gesprungen werden soll. Interessant wir Seiten auf denen viele Inhalte sind.
  • format (Standard: html): Gibt an um welches Format es sich bei der Zielseite handelt. Alternativ ginge auch "xml", obwohl das bei einer Formularzielseite wenig Sinn machen würde. Klappt nur wenn actionUri nicht gesetzt ist.
  • additionalParams: Fügt weitere Variablen der Zielseite an. Im Gegensatz zu arguments, können hiermit Variablen hinzugefügt werden die nicht mit dem Extensionnamen geprefixed werden. Klappt nur wenn actionUri nicht gesetzt ist.
  • absolute (Standard: deaktiviert): Nach Aktivierung wird der Zeilseite noch der Domainname und Pfad vorangestellt. Klappt nur wenn actionUri nicht gesetzt ist.
  • addQueryString: Falls dem Formular bereits Parameter über die URL mitgegeben wurden, könnt Ihr hier nun entscheiden, ob diese Parameter auch mit auf die Zielseite übergeben werden. Klappt nur wenn actionUri nicht gesetzt ist.
  • argumentsToBeExcludedFromQueryString: Falls Ihr addQueryString aktiviert habt, aber einen oder zwei bestimmte Parameter wieder entfernen wollt, dann tragt Ihr hier diese Parameter ein. Klappt nur wenn actionUri nicht gesetzt ist.
  • fieldNamePrefix (Standard: tx_extensionName_pluginName): Falls ein anderer Prefix gewünscht ist. Macht eigentlich nur Sinn, wenn die Formulardaten von einer anderen Extension abgearbeitet werden müssen.
  • actionUri: Gebt hier Eure ganz eigene individuelle Zielseiten-URL ein. Viele der oberen Parameter haben aber dann keinen Wirkung mehr.
  • objectName: Hier kommt ein Objekt- bzw. Modelname rein, in das die nach Absenden gesammelten Formulardaten gespeichert werden sollen. Hat den Vorteil, dass Ihr nicht in jeder Action die Formulardaten überprüfen, sondern die Überprüfung nur einmalig im Model vornehmen müsst.

Der f:form-ViewHelper schaut von seinen ganzen Parametern extremst gewaltig aus. Aber wenn man mal bedenkt das allein 11 Parameter nur für die Generierung der Zielseiten-URL zuständig sind, bleibt nur noch eine handvoll Parameter übrig. Der große Vorteil diesen ViewHelpers ist Sicherheit und Arbeitserleichterung, die wir uns in den Beispielen mal näher anschauen.

Beispiel

 

<f:form object="{feUser}" objectName="newFeUser">
    <f:form.textarea property="firstName" rows="5" cols="50" />
</f:form>

 

Ich hab grad meinen Rechner frisch aufgesetzt und hab leider noch keine Extension hier mit der ich das testen konnte. Deshalb schaut das Ergebnis im FE-Quelltext ein bisschen "unnormal" aus:

 

<form action="/typo3_46/index.php?id=6&amp;tx__%5Bcontroller%5D=Standard&amp;cHash=d1469ddb628871564f3257920c1f6ee8" method="post"> <div style="display: none"> <input type="hidden" name="__referrer[extensionName]" value="" /> <input type="hidden" name="__referrer[controllerName]" value="Standard" /> <input type="hidden" name="__referrer[actionName]" value="index" /> <input type="hidden" name="__hmac" value="a:2:{s:9:&quot;newFeUser&quot;;a:1:{s:9:&quot;firstName&quot;;i:1;}s:4:&quot;tx__&quot;;a:1:{s:10:&quot;controller&quot;;i:1;}}ff5ff9b62f7b5c49a696d3f7b1009991853d6533" /> </div> <textarea rows="5" cols="50" name="newFeUser[firstName]"></textarea> </form>

 

Hier seht Ihr das Thema Sicherheit. Fluid baut automatisch einen versteckten Bereich mit ein paar Werten in Euer Formular ein. Unteranderem befindet sich dort ein __hmac-Wert. Innerhalb diesen Wertes sind alle erlaubten Formularfelder nochmals enthalten. Wenn also über bestimmte Webseitenattacken Felder entfernt oder hinzugefügt werden, dann kann Extbase später feststellen, dass die Anzahl und/oder Feldnamen nicht übereinstimmen und wirft eine Fehlermeldung. Das Formular kann also nicht abgesendet werden.

Ich habe in meinem Beispiel noch einen Objektnamen angegeben "newFeUser". Wie Ihr seht wurde dieser Wert jedem Feld in meinem Formular vorangestellt. Das hat den Vorteil, dass meine Formularfelder nicht einzeln, sondern gebündelt in einem Array an die Zielseite übertragen werden. Dort angekommen kann ich meiner Action mitteilen, dass der Inhalt diesen Arrays in ein Modell portiert werden soll. Bei dieser Portierung werden auch automatisch die enthaltenen Werte auf Gültigkeit überprüft, sofern Ihr überhaupt Überprüfungsregeln in Euren Modellen angegeben habt. Nur wenn alle Überprüfungen gültig waren, kann das Modell mit den Formulardaten an die Action übergeben werden und dort mit einem Einzeiler in der Datenbank gespeichert werden.

f:groupedFor

Parameter

  • each (Pflichtangabe): Array oder Objekt, das durchlaufen werden soll
  • as (Pflichtangabe): Ein Variablenname, der die gruppierten Datensätze enthält
  • groupBy: anhand welcher Eigenschaft soll das Array gruppiert werden
  • groupKey: Innerhalb der f:groupedBy-Tags kann mit dieser Variable auf den gruppierten Wert zugegriffen werden.

Ein sehr mächtiger ViewHelper im Bereich der Listengenerierung. Übergebt dem ViewHelper ein Array und ein Gruppierungskriterium und Ihr erhaltet mit jedem Durchlauf bzw. mit jeder gefundenen Gruppe ein Array mit den dazugehörigen Arrayelementen zurück. Zu kompliziert? Na dann schaut mal in die Beispiele:

Beispiel

 

<f:alias map="{mitarbeiter: {0: {vorname: 'Stefan', stadt: 'Lindlar'},1: {vorname: 'Petra', stadt: 'Lindlar'},2: {vorname: 'Sascha', stadt: 'Remscheid'},3: {vorname: 'Patrick', stadt: 'Bonn'},4: {vorname: 'Sven', stadt: 'Gummersbach'},5: {vorname: 'Andrea', stadt: 'Wuppertal'}}}">
    <table cellpadding="5" cellspacing="0" border="2">
        <f:groupedFor each="{mitarbeiter}" as="kollegen" groupBy="stadt" groupKey="stadt">
            <tr>
                <th colspan="2">{stadt}</th>
            </tr>
            <f:for each="{kollegen}" as="kollege">
                <tr>
                    <td style="color: {color}">{kollege.vorname}</td>
                    <td style="color: {color}">{kollege.stadt}</td>
                </tr>
            </f:for>
        </f:groupedFor>
    </table>
</f:alias>

 

Ich hab hier wieder das Beispiel mit den 6 Mitarbeitern ausgepackt. Wie Ihr sehen könnt habe ich dem f:groupedBy-ViewHelper gesagt, dass er diese Mitarbeiter anhand ihrer Städte gruppieren soll (groupBy). Das geübte Auge sieht evtl. sofort, dass die ersten beiden Arrayeinträge in Lindlar wohnen. Um innerhalb der f:groupedBy-Tags auf diesen Städtenamen zugreifen zu können, verwende ich den Parameter groupKey. Der Wert aus groupBy und groupKey müssen nicht übereinstimmen. Innerhalb von groupKey hätte ich auch einfach city nehmen können.

Über die Variable, die ich unter "as" angegeben habe kann ich nun auf die Elemente der ersten gefundenen Gruppe zugreifen und mit f:for durchlaufen lassen.

f:if

Parameter

  • condition (Pflichtangabe): Hier kommt die Vergleichtsabfrage rein (Siehe Beispiele)

Ich glaube der f:if-ViewHelper ist mit der wichtigste ViewHelper überhaupt auch wenn er in seinen Möglichkeiten etwas beschränkt ist. Wer sich mal den fed:if-ViewHelper aus der fed-Extension angeschaut hat, weiß wovon ich rede.

Folgende Vergleiche sind erlaubt: ==, !=, <, <=, >, >= und %

Vergleicht werden können nur Variablen folgenden Typs: Zahlen, Objekteigenschaften, Arrays und Ergebnisse aus ViewHelpern

Einfache Beispiele

 

<f:alias map="{wert1: 1, wert5: 5}">
    <f:if condition="{wert1}==1">
        <p>Der Wert ist 1</p>
    </f:if>
    <f:if condition="{wert5}==5">
        <f:then>
            <p>Der Wert ist 5</p>
        </f:then>
        <f:else>
            <p>Der Wert ist NICHT 5</p>
        </f:else>
    </f:if>
    <f:if condition="{wert5} % 2">
        <f:then>
            <p>Die Berechnung liefert einen Restwert von 1.</p>
        </f:then>
        <f:else>
            <p>Es konnte kein Restwert ermittelt werden</p>
        </f:else>
    </f:if>
    <f:if condition="{wert1}!={wert5}">
        <p>wert1 ist NICHT gleich wert5</p>
    </f:if>
    <f:if condition="{wert1}<{wert5}">
        <p>wert1 ist kleiner als wert5</p>
    </f:if>
    <f:if condition="{0:wert1,1:wert5}=={0:1,1:5}">
        <p>Der erste Array ist gleich mit allen Werten aus dem zweiten Zahlenarray</p>
    </f:if>
</f:alias>
<f:alias map="{elemente: {0: wert1, 1: wert2}}">
    <f:if condition="{elemente -> f:count()==2">
        <p>Vergleich mit ViewHelpern: Das Elementearray beinhaltet 2 Elemente</p>
    </f:if>
</f:alias>
<f:alias map="{wert1: 'hallo'}">
    <f:if condition="{0: wert1} == {0: 'hallo'}">
        <p>Stringvergleiche klappen nur als Array</p>
    </f:if>
</f:alias>

 

Wenn kein f:then oder f:else-ViewHelper gefunden wurde, dann wird der Inhalt zwischen den f:if-ViewHelpern immer nur dann ausgegeben, wenn die Bedingung wahr ist. Wenn komplette WENN->DANN-SONST-Konstrukte erzeugt werden müssen, dann müssen auch immer die f:then und f:else-ViewHelper verwendet werden.

f:image

Parameter

  • src (Pflichtangabe): Pfad zu der Datei. Hier kann auch mit EXT: gearbeitet werden, da es sich hier um ein IMG_RESOURCE handelt
  • alt (Pflichtangabe): Alternativtext für das Bild, falls es nicht angezeigt werden kann/darf
  • width: Breite des Bildes. Hier kann z.B. mit einem angehangenen "c" gesagt werden, dass das Bild, falls die Proportionen nicht genau passen geschnitten wird. Z.B. 200c
  • height: Höhe des Bildes. Hier kann z.B. mit einem angehangenen "c" gesagt werden, dass das Bild, falls die Proportionen nicht genau passen geschnitten wird. Z.B. 100c
  • minWidth: Auch wenn die Bilder im Original evtl. kleiner sind wie dieser Wert hier, so werden diese Bilder auf die hier angegebene Breite gezoomt.
  • minHeight: Auch wenn die Bilder im Original evtl. kleiner sind wie dieser Wert hier, so werden diese Bilder auf die hier angegebene Höhe gezoomt.
  • maxWidth: Auch wenn die Bilder im Original evtl. größer sind wie dieser Wert hier, so werden diese Bilder auf die hier angegebene Breite verkleinert.
  • maxHeight: Auch wenn die Bilder im Original evtl. größer sind wie dieser Wert hier, so werden diese Bilder auf die hier angegebene Höhe verkleinert.

Ich war total begeistert, als ich von diesem ViewHelper das erstmal gelesen habe. Die Arbeit, die man sich normalerweise umständlich in einer Extension oder im TS machen musste gibt es nun fertig als ViewHelper und die Bilder werden nicht einfach nur verkleinert dargestellt. Nein! Sie werden mit Hilfe von PHP-GD und/oder imagemagick auf die hier angegebene Größe verkleinert. Eine geniale Erfindung, die ich nicht mehr missen möchte.

Beispiel in Originalgröße

<f:image src="fileadmin/bilder/landschaft.jpg" alt="landschaft" />

Beispiel: Beibehaltung der Proportionen

<f:image src="fileadmin/bilder/landschaft.jpg" alt="landschaft" width="50" />

Beispiel: Geschnittenes Bild

<f:image src="fileadmin/bilder/landschaft.jpg" alt="landschaft" width="100c" height="100c" />

Die kürzere Seite wird auf 100 Pixel gesetzt und bei der längeren Seite wird nach 100 Pixeln einfach abgeschnitten.

f:layout

Doku kommt noch

f:renderChildren

Parameter

  • arguments: Variablen, die an das Template übergeben werden sollen

Viele Informationen habe ich noch nicht über diesen ViewHelper. Laut der Doku innerhalb diesen ViewHelpers steht aber, dass dieser ViewHelper nur für Templates verwendet werden kann, die sich auf einen Widget Controller beziehen.

f:render

Parameter

  • section: Der Name einer Section, die gerendert werden soll.
  • partial: Pfad + Dateiname ohne .html-Endung ab dem Verzeichnis, das für Partials definiert wurde.
  • arguments: Welche Variablen sollen in den Partial/das Layout übernommen werden.
  • optional (Standard: deaktiviert): Normalerweise hagelt es Fehlermeldungen, wenn Sections nicht auffindbar sind. Setzt man diesen Parameter aber auf TRUE, dann wird eben nichts ausgegeben.

Die Partials sind in Fluid wie die FCEs in TemplaVoila. Kurz: Wiederverwendbare Templates. Eine geniale Sache solange Ihr diese ViewHelper in Maßen einsetzt, denn das Laden eines Partials dauert ca. 5 Millisekunden. Wenn Ihr also irgendwann mal auf die Idee kommen solltet jede Zelle einer Tabelle mit Partials generieren zu wollen, dann kann das Laden der Webseite bei 700 Tabellenzeilen und 15 Spalten schonmal etwas dauern: 700 Zeilen * 15 Spalten * 5 Millisekunden = 52500 Millisekunden. Plus die Zeit, die TYPO3 selbst noch braucht sind wir bei knapp einer Minute.

Die Dateien für Partials liegen immer in fest vorgegebenen Verzeichnissen. Innerhalb von Extensions ist dies: typo3conf/ext/[ExtensionKey]/Resources/Private/Partials/. Wenn Ihr allerdings mit FLUIDTEMPLATE arbeitet, dann gebt Euren gewünschten Verzeichnispfad mit Hilfe der TS-Eigenschaft "partialRootPath" mit abschließendem / an.

Sections haben kein eigenes Verzeichnis, da diese immer innerhalb der aktuellen Templatedatei definiert werden müssen. Außnahmen machen da die Layouts.

Beispiel für Partial

In unserem Fluidtemplate:

 

<f:alias map="{mitarbeiter: {0: {vorname: 'Stefan', stadt: 'Lindlar'},1: {vorname: 'Petra', stadt: 'Lindlar'},2: {vorname: 'Sascha', stadt: 'Remscheid'},3: {vorname: 'Patrick', stadt: 'Bonn'},4: {vorname: 'Sven', stadt: 'Gummersbach'},5: {vorname: 'Andrea', stadt: 'Wuppertal'}}}">
    <table cellpadding="5" cellspacing="0" border="2">
        <f:for each="{mitarbeiter}" as="kollege">
            <f:render partial="TableRow" arguments="{kollege: kollege}"/>
        </f:for>
    </table>
</f:alias>

 

In der Partial Datei TableRow.html

 

<tr>
    <td>{kollege.vorname}</td>
    <td>{kollege.stadt}</td>
</tr>

 

Weitere Beispiele folgen

f:section

Parameter

  • name (Pflichtangabe): Ein Name unter dem man den Inhalt zwischen den Tags wieder auffinden kann

Sections sind ähnlich Partials mit dem Unterschied, dass Partials in Dateien ausgelagert werden und Sections innerhalb des Templates definiert werden müssen.

Beispiel

 

<f:section name="TableRow">
    <tr>
        <td>{kollege.vorname}</td>
        <td>{kollege.stadt}</td>
    </tr>   
</f:section>
<f:alias map="{mitarbeiter: {0: {vorname: 'Stefan', stadt: 'Lindlar'},1: {vorname: 'Petra', stadt: 'Lindlar'},2: {vorname: 'Sascha', stadt: 'Remscheid'},3: {vorname: 'Patrick', stadt: 'Bonn'},4: {vorname: 'Sven', stadt: 'Gummersbach'},5: {vorname: 'Andrea', stadt: 'Wuppertal'}}}">
    <table cellpadding="5" cellspacing="0" border="2">
        <f:for each="{mitarbeiter}" as="kollege">
            <f:render section="TableRow" arguments="{kollege: kollege}"/>
        </f:for>
    </table>
</f:alias>

 

Wie Ihr seht definiere ich erst das HTML für eine Tabellenzeile und gebe dieser Section den Namen "TableRow". Später dann in meiner Schleife rufe ich mit Hilfe des f:render-ViewHelpers diese Section auf.

f:then

Diesen ViewHelper erkläre ich im Bereich f:if, da er nur dort verwendet werden kann.

f:translate

Parameter

  • key (Pflichtangabe): Der key, mit dem man die Übersetzung aus dem Sprachdateien auslesen kann
  • default: Wenn der Key in der Sprachdatei nicht gefunden werden kann, dann verwende diesen Text. Wenn default nicht gesetzt ist, wird der Inhalt zwischen den Tags verwendet.
  • htmlEscape (Standard: aktiviert): Alle Übersetzungen aus den Sprachdateien werden durch htmlspecialchars geschleust, was es unmöglich macht, HTML-Tags in den Übersetzungen anzeigen zu lassen. Setzt diesen Wert auf FALSE, um dieses Vorgehen zu unterbinden.
  • arguments: In den Übersetzungen können Platzhalter definiert werden, die dann mit den Inhalten diesen Arrays gefüllt werden.

Mit dem f:translate-ViewHelper greift Ihr auf eine beliebige Sprachdatei (meist locallang.xml) zu und holt Euch die entsprechende Übersetzung mit Hilfe der Angabe im Parameter "key".

Innerhalb von Extensions wird immer auf die locallang.xml im Verzeichnis Resources/Private/Language/ zugegriffen. Im Bereich FLUIDTEMPLATE müsste Ihr hier die Pfadsyntax verwenden:

 

LLL:fileadmin/templates/locallang.xml:domain_model_irgendwas.titel

 

bzw:

 

LLL:EXT:meineExtension/Resources/Private/Language/locallang.xml:domain_model_irgendwas.titel

 

Diese "LLL:" Prefixe müssen bei der Pfadnotation immer gesetzt sein!!! Etwas einfacher machen es sich die FLUIDTEMPLATE-User, wenn sie im TS zuvor angeben auf welche locallang.xml welcher Extension sie zugreifen wollen:

 

extbase.pluginName = Pi1
extbase.controllerExtensionName = MeineExtension

 

Dann reicht es auch wieder nur den "key" anzugeben OHNE den ganzen Pfad

Beispiel

 

<f:translate key="domain_model.title" htmlEscape="false" />

 

Beispiel mit Pfad

 

<f:translate key="LLL:fileadmin/lang/locallang.xml:domain_model.title" />

 

Beispiel mit Platzhaltern

In unserem Template:

 

<f:translate key="LLL:fileadmin/lang/locallang.xml:domain_model.title" arguments="{0: 'Herr der Ringe'}" />

 

In der locallang.xml

 

<label index="domain_model.title">Title of: %s</label>

 

Mit %s wird auf den ersten Wert des übergebenen Arrays zugegriffen. Kommt %s nochmals vor, dann wird auf den zweiten Arrayeintrag zugegriffen. Um das unabhängig von der Reihenfolge zu machen empfehle ich noch folgende Notation:

 

<label index="domain_model.title">Titel von: %1$s</label>

 

Mit %1 greift Ihr auf den ersten Eintrag zu und sagt diesem, dass er als String/Text interpretiert werden soll ($s). Auf php.net findet Ihr heraus wofür diese ganzen Kürzel stehen.