Du hast deinen AdBlocker an?

Es wäre ein Traum, wenn du ihn für t3n.de deaktivierst. Wir zeigen dir gerne, wie das geht. Und natürlich erklären wir dir auch, warum uns das so wichtig ist. Digitales High-five, deine t3n-Redaktion

t3n 19

Die wahre Macht der neuen Template-Engine: Fluid-ViewHelper in der Praxis

Sebastian Kurfürst hat Extbase – der neuen, modernen und zukunftssicheren Art, Extensions für TYPO3 zu programmieren – gleich auch noch eine neue Template-Engine namens Fluid zur Seite gestellt. Ursprünglich für FLOW3 entwickelt, wurde die Engine auch für TYPO3 zurückportiert. Die wahre Macht von Fluid liegt dabei nicht in der zugegebenermaßen sehr einfachen Syntax, sondern in den ViewHelpern, mit denen Entwickler die Template-Engine nahezu beliebig erweitern können. Wir stellen die ViewHelper vor und zeigen, wie man eigene programmiert.

Während bei der klassischen Extension-Programmierung allerorten mit Markern, Subparts und wenig intuitiven Funktionen gearbeitet wird, um die Ausgabe zu erzeugen, war spätestens mit der Entwicklung von FLOW3 der Wunsch geboren, eine leistungsfähigere und intuitivere Templating-Engine ins Leben zu rufen, die vor allem den folgenden Anforderungen genügen sollte:

  • Die Templating-Engine soll einfach und elegant zu verwenden sein.
  • Es sollen unterschiedliche Ausgabeformate unterstützt werden, nicht nur HTML.
  • Die Engine soll den Template-Editor in vielerlei Hinsicht unterstützen, beispielsweise soll Auto-Completion in gängige IDEs möglich sein.
  • Programmiercode und Ausgabe müssen komplett getrennt sein.
  • Es soll ein einfaches und klares Interface existieren, mit dem man die Engine leicht erweitern kann.

Gerade der letzte Punkt gibt die Richtung vor: Die Entwickler von Fluid haben von Anfang an darauf geachtet, eine möglichst modular erweiterbare Templating-Engine zu entwerfen. Mit den ViewHelpern, die letztlich einfach eigene Klassen sind, wurde dies perfekt umgesetzt.

Jetzt geht’s los – Vorbereitung

Damit Sie die nachfolgenden Beispiele allesamt gleich ausprobieren können, müssen Sie sich zunächst eine kleine Testumgebung aufsetzen. Dafür benötigen Sie TYPO3-Version 4.3 oder höher. Installieren Sie dort im Extensionmanager die Extensions „extbase“ und „fluid“ und zusätzlich die Extension „efempty“ (Extbase Fluid Empty), die eine „leere“ Extension mit einem rudimentären Model, einem Controller (StartController), einer Action (index) und einem dazugehörigen View zur Verfügung stellt. Während die erstgenannten Sie in diesem Artikel zunächst überhaupt nicht interessieren müssen, konzentrieren wir uns in diesem Kapitel vornehmlich auf den View.

Platzieren Sie zunächst die Extension „efempty“ auf einer Seite (für die eine Ausgabe definiert ist, beispielsweise durch „css_styled_content“), indem Sie das Content-Element „Plugin“ auswählen und als Plugin-Typ „Empty Extbase/Fluid Container“ angeben. Nach dem Abspeichern ist die Extension einsatzfähig und sollte die Ausgabe „Hello World“ erzeugen.

Für die weiteren Ausführungen benötigen Sie im Grunde zwei Dateien: die Controller-Datei „StartController.php“ im Verzeichnis „typo3conf/ext/efempty/Classes/Controller/“ und die View-Datei index.html im Verzeichnis „typo3conf/ext/efempty/Resources/Private/Templates/Start/“.

Die Controller-Datei ist dafür zuständig, die Ausgabe (genauer gesagt den View) mit Inhalten zu versorgen, während die View-Datei diese (mit Hilfe der ViewHelper) anzeigt. Dahinter steckt ein viel umfassenderes Prinzip, das als MVC (Model View Controller) bezeichnet wird und beispielsweise unter [1] näher erläutert wird.

Fluid? – Eine kurze Einführung

In t3n 16 [2] finden Sie einen ausführlichen Artikel zu Fluid, im Folgenden wird nur kurz auf die Syntax von Fluid eingegangen.

In der Controller-Datei – genauer in der Index-Action (die Methode mit dem Namen „IndexAction“) – wird nun über die Methode „assign()“ ein Wert an den View übergeben:

Zuweisung mittels assign() im StartController.php 

public function indexAction() {
	...
		$this->view->assign('helloworld','Hello world!');
	...
}

Listing 1

Die entscheidende Stelle ist im obigen Codebeispiel hervorgehoben. Die Methode „assign“() hat dabei zwei Parameter – über den ersten wird der Name festgelegt, unter dem der Wert der Zuweisung später im Template angesprochen werden kann, und über den zweiten Parameter wird der Wert selbst spezifiziert. Im obigen Beispiel wird also der Wert „Hello world!“ an den Bezeichner „helloworld“ übergeben.

Nun betrachten wir die View-Datei, um zu sehen, wie das Ganze verarbeitet und ausgegeben wird:

Ausgabe des Bezeichners in index.html 

<h1>This is the efempty extension!</h1>
<h2>It works :-)</h2>
{helloworld}

Listing 2

Der Bezeichner wird also lediglich in geschweifte Klammern geschrieben, schon sorgt Fluid automatisch dafür, dass einerseits nachgesehen wird, welcher Wert für diesen Bezeichner vorhanden ist und andererseits, dass der Wert ausgegeben wird.

Um nun beispielsweise Arrays auszugeben, verwendet Fluid die so genannte Punkt-Syntax (oder auch Objektzugriffs-Syntax). So kann über den Bezeichner mit dem Punkt auf den numerischen Index von Array zugegriffen werden. In den folgenden Codebeispielen werden der Übersichtlichkeit halber jeweils oben die entscheidenden Codezeilen in der Action und darunter der Code im Template notiert.

Ausgabe eines numerischen Arrays 

// Code in der IndexAction des Start-Controllers $helloarray = array('Hello','World!');
$this->view->assign('helloworld',$helloarray); 

// Code im Index-Template
{helloworld.0} {helloworld.1}

Listing 3

Man kann also über die Punktschreibweise ganz einfach auf den numerischen Index zugreifen. Für einen assoziativen Index funktioniert dies ähnlich:

Ausgabe eines assoziativen Arrays 

// Code in der IndexAction des Start-Controllers $helloarray = array('hello' => 'Hello', 'world' => 'World!');
$this->view->assign('helloworld',$helloarray); 

// Code im Index-Template
{helloworld.hello} {helloworld.world}

Listing 4

Hier wird einfach der assoziative Index angegeben, um darauf zuzugreifen. Mehrdimensionale Arrays erreichen Sie dementsprechend ganz ähnlich. Sie müssen nur anstelle der Klammern in PHP Punkte notieren: „$helloarray['foo'][1]['bar']“ wird somit zu „{helloworld.foo.1.bar}“.

Da Fluid grundsätzlich objektorientiert arbeitet, ist auch in diesem Bereich gute Unterstützung zu erwarten. Und tatsächlich ist das Ansprechen eines Objekts beziehungsweise dessen Eigenschaften im Grunde exakt identisch zur Ansprache als Array.

Ausgabe eines Objekts 

// Code in der IndexAction des Start-Controllers
$start = new Tx_Efempty_Domain_Model_Start;
$start->setTitle("Hello World!");
$this->view->assign('helloworld', $start);

 // Code im Index-Template
{helloworld.title}

Listing 5

Die Extension „efempty“ enthält ja genau ein Model mit dem Namen Start, das als Klasse „Tx_Efempty_Domain_Model_Start“ manifestiert wurde. Dort gibt es eine Eigenschaft „title“, die wiederum wird mit dem Setter „setTitle“() gesetzt. Nun wird also das ganze Objekt samt gesetztem Titel an den View übergeben, dort wird der Titel mittels „{helloworld.title}“ ausgegeben.

Das klingt zunächst logisch und nachvollziehbar – ist aber nur die halbe Wahrheit. Im Model wird die Eigenschaft „title“ nämlich als „protected“ gekennzeichnet – das heißt ein direkter Zugriff darauf ist nicht möglich. Daher wird auch im obigen Fall die Eigenschaft nicht direkt ausgelesen, sondern stattdessen der im Model vorhandene Getter „getTitle“() verwendet.

Auch bei der Objektansprache ist es einfach, auf Objekte zuzugreifen, die lediglich Unterobjekte sind. Trennen Sie diese einfach wieder mit einem Punkt. So könnte man mittels „{blog.post.comment.0}“ auf den ersten Kommentar eines Posts zugreifen, der wiederum in einem Blog liegt.

Das ist schon alles, was Sie über die Basis-Syntax von Fluid wissen müssen – das ist der so genannte Core (auch Kern genannt). Alle weiteren Funktionalitäten werden von so genannten ViewHelpern übernommen, die wir uns im Folgenden näher ansehen.

Fluid-ViewHelper-Basis-Syntax

Sämtliche Ausgabelogik wird von speziellen Tags, den so genannten ViewHelpern, übernommen. Diese sorgen für Kontrollstrukturen, Schleifen, Links und ähnliche benötigte Funktionalitäten innerhalb des Templates.

Die Grundsyntax dabei ist wie folgt (beachten Sie, dass das kleine „f“ und der Doppelpunkt sozusagen den Namensraum von Fluid angeben):

ViewHelper-Basis-Syntax (verwendet im Template) 

<f:ViewHelperName Parameter>...</f:ViewHelperName>

Listing 6

Dabei entspricht jeder Tag (also jeder ViewHelper) einer eigenen ViewHelper-Klasse, die sich bei den mit Fluid mitgelieferten ViewHelpern im Verzeichnis „typo3conf/ext/fluid/Classes/ViewHelpers“ befindet.

So gibt es etwa den If-Viewhelper, der mit „<f:if ...>...</f:if>“ spezifiziert wird. Die zugehörige Datei befindet sich also exakt hier: „typo3conf/ext/fluid/Classes/ViewHelpers/IfViewHelper.php“.

Für einige ViewHelper sind komplexere Aufgaben vorgesehen (beispielsweise bei der Link-Erzeugung oder bei Formularen). Hier wird ein eigenes, gleich benanntes Unterverzeichnis im ViewHelpers-Verzeichnis angelegt, das die zugehörigen ViewHelper enthält. Angesprochen werden diese dann über die Punktsyntax. So wird beispielsweise der Action-Link „<f:link.action ...>...</f:link.action>“ über die Datei „typo3conf/ext/fluid/Classes/ViewHelpers/Link/ActionViewHelper.php“ verwaltet.

ViewHelper-Argumente

Bei einigen ViewHelpern kann man Argumente übergeben, beispielsweise bei Links oder Formularen. Die Notation für solche Argumente folgt dabei der JSON-Syntax, die unter Umständen aus JavaScript bekannt ist. Dabei gilt folgende Festlegung:

  • Ein Objekt beginnt mit { und endet mit }. Es kann eine durch Kommata geteilte, ungeordnete Liste von Eigenschaften enthalten.
  • Eine Eigenschaft besteht aus einem Schlüssel und einem Wert, getrennt durch einen Doppelpunkt.
  • Ein Schlüssel ist eine Zeichenkette.
  • Ein Wert ist ein Objekt, ein Array, eine Zeichenkette, eine Zahl oder einer der Ausdrücke TRUE oder FALSE.
Beispiele für Argumente 

// über den Parameter „arguments“ wird ein Argument übergeben
<f:link.action controller="Blog" action="show" arguments="{id :blogPost.id}">
	... read more
</f:link.action>

// zwei oder mehr Argumente sind natürlich ebenfalls möglich <f:link.action controller="Post" action="edit" arguments="{post : post, blog : blog}">
Edit
</f:link.action>

Listing 7

If-ViewHelper

Zusammen mit dem Then- und Else-Viewhelper können Sie eine komplette If-Then-Else-Abfrage realisieren, wie Sie sie aus den klassischen Programmiersprachen kennen. Dazu wird im If-Viewhelper eine Bedingung geprüft und der Then-Zweig angesprungen, wenn die Prüfung positiv war. Sollte die Bedingung negativ ausfallen, wird direkt im Else-Zweig weitergemacht.

Der If-Then-Else-ViewHelper 

// Verwendung
<f:if condition="{blog.posts}">
	<f:then>
		<p>Es sind Posts vorhanden!!</p>
	</f:then>
	<f:else>
		<p>Keine Posts vorhanden!!</p>
	</f:else>
</f:if>

Listing 8

Die Ausgabe des obigen Codebeispiels ist nun abhängig davon, ob das im Argument „condition“ angegebene Blog Beiträge (Posts) besitzt. Ist dies der Fall, wird die Zeichenkette „Es sind Posts vorhanden!!“ ausgegeben. Ansonsten erfolgt die Ausgabe der Zeichenkette im Else-Zweig.

For-ViewHelper

Der For-ViewHelper ist genau genommen (noch) ein Foreach-ViewHelper, da dieser die foreach()-Funktion von PHP nachbildet. Über das Argument „as“ wird ein neuer Name für das aktuelle Schleifenobjekt festgelegt, den man ganz normal ansprechen kann. Über das Argument „key“ kann man auf den Schlüssel zugreifen.

Der For-ViewHelper 

// Verwendung
<f:for each="{0:1, 1:'a', 2:'b', 3:4}" as="wert">{wert}</f:for>
// Ausgabe
1ab4

Listing 9

Link-ViewHelper

Über diese ViewHelper-Familie können bequem Links mit zusätzlichen Funktionalitäten angelegt werden. Beispielsweise sorgt der Link-Action-ViewHelper dafür, korrekte Extbase-Action-Links zu erzeugen. Sie müssen dazu nur die Action und den Controller angeben, Fluid sorgt dann automatisch dafür, dass der richtige Link erstellt und ausgegeben wird.

Link-Action-ViewHelper 

// Verwendung
<f:link.action action="show" controller="Start">
	Link auf die Show-Action des Start-Controllers
</f:link.action> 

// Ausgabe <a href="index.php?id=6&amp;tx_efempty_pi1[action]=show&amp;tx_efempty_pi1[c ontroller]=Start&amp;cHash=75b5377af6">Link auf die Show-Action des Start-Controllers</a>

Listing 10

Weitere Beispiele sind hier der Link-Email-ViewHelper (zur Erzeugung von E-Mail-Links), der Link-External-ViewHelper (zur Erzeugung von externen Links) oder der Link-Page-ViewHelper (zur Erzeugung von Links auf interne Seiten).

Weitere Link-ViewHelper 

// Verwendung Link-Email-ViewHelper
<f:link.email email="patrick.lobacher@typofaktum.de">E-Mail an Patrick Lobacher schreiben</f:link.email> 

// Ausgabe Link-ViewHelper
<a href="mailto:patrick.lobacher@typofaktum.de">E-Mail an Patrick Lobacher schreiben</a>

// Verwendung Link-Page-ViewHelper
<f:link.page pageUid="1" additionalParams="{parameter: 'wert'}">Link auf die Seite mit der UID=1</f:link.page> 

// Ausgabe Link-Page-ViewHelper
<a href="index.php?id=1&amp;parameter=wert&amp;cHash=ff9bd32e54">Link auf die Seite mit der UID=1</a>

Listing 11

Bei den Link-ViewHelpern bekommen Sie die komplette URL zurück. Ist dies nicht gewünscht, kann anstelle dessen der Uri-ViewHelper verwendet werden, der genauso angesprochen wird, aber nur den Link an sich zurückliefert.

Translate-ViewHelper

Über diesen ViewHelper können mehrsprachige Labels verwendet werden. Dafür werden die aktuelle Sprache aus der aktuellen TypoScript-Konfiguration ermittelt und dann die zugehörige „locallang.xml“-Datei (oder „locallang.php“) im Verzeichnis „Resources/Private/Language/“ der Extension ermittelt. Dort wird über den Parameter „key“ der entsprechende Schlüssel ausgelesen und dessen Wert zurückgegeben.

Der Translate-ViewHelper 

// Aufbau locallang.xml
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<T3locallang>
	<meta type="array">
		<type>module</type>
		<description>Sprachlabels für die Efempty Extension</description>
	</meta>
<data type="array">
		<languageKey index="default" type="array">
			<label index="das.ist.der.key">This is the english output</label>
		</languageKey>
		<languageKey index="de" type="array">
			<label index="das.ist.der.key">Das ist die deutsche Ausgabe</label>
		</languageKey>
	</data>
</T3locallang> 

// Verwendung
<f:translate key="das.ist.der.key" /> 

// Ausgabe (bei Konfiguration der deutschen Sprache)
Das ist die deutsche Ausgabe

Listing 12

Format-ViewHelper

Die ViewHelper in dieser Gruppe sind primär dafür zuständig, einen Eingabewert in ein anderes Format umzuwandeln. Hier gibt es den Format-Crop-ViewHelper (der einen Eingabestring nach einer Anzahl Zeichen abschneidet), Format-Currency-ViewHelper (zur Formatierung von Währungen), Format-Date-ViewHelper (zur Formatierung eines Datums), Format-Html-ViewHelper (zur Formatierung eines HTML-Strings mit Hilfe der TYPO3-Parsefunc-Funktion) und einige andere Formatierungs-ViewHelper.

Beispiele für Format-ViewHelper 

// Verwendung Format-Crop-ViewHelper
<f:format.crop maxCharacters="15">Das ist ein sehr langer Text!</f:format.crop>
<f:format.crop maxCharacters="15" respectWordBoundaries="true"
append="&nbsp;[mehr]">Das ist ein sehr langer Text!</f:format.crop>
// Ausgabe Format-Crop-ViewHelper
Das ist ein...
Das ist ein [mehr]

// Verwendung Format-Html-ViewHelper
<f:format.html>String1 <b>String2</b>. Das ist <LINK 1>ein Link</LINK>.</f:format.html>
// Ausgabe Format-Html-ViewHelper
<p class="bodytext">String1 <b>String2</b>. Das ist <a href="index.php?id=1" >ein Link</a>.</p>

Listing 13

Weitere ViewHelper

Zusätzlich gibt es noch einige weitere wichtige ViewHelper, beispielsweise cObject (gibt TypoScript aus), image (kümmert sich um die formatierte Ausgabe von Bildern), count (gibt die Anzahl aus), cycle (ermöglicht eine Rotation beispielsweise für iterierende Darstellung), form (zur Ausgabe von Formularen), render (mit Hilfe dessen man Layouts, Partials und Sektionen darstellen kann) und viele mehr. Eine Übersicht über die zur Verfügung stehenden ViewHelper und deren Syntax finden Sie in der Dokumentation [3].

Eigene ViewHelper erstellen

In einem Projekt kommt es oft genug vor, dass zwar das Design schon halbwegs abgesegnet ist und bereits implementiert werden kann – dann allerdings fehlen zumeist die Texte, um beispielsweise die Boxen in den Randspalten zu füllen. Um nun nicht warten zu müssen, bis der Kunde die Inhalte liefert, kann man dort Dummy-Texte platzieren.

Und genau dies soll der Dummytext-ViewHelper erledigen, der als einfaches Beispiel für einen selbstgeschriebenen ViewHelper dient. Zunächst benötigen Sie eine Datei „DummytextViewHelper.php“ im Verzeichnis „Classes/ViewHelpers/“, das Sie im Verzeichnis der vorhin schon benutzten Extension „efempty“ anlegen.

DummytextViewHelper.php 

<?php
	class Tx_Efempty_ViewHelpers_DummytextViewHelper extends
Tx_Fluid_Core_ViewHelper_AbstractViewHelper {
	/**
	* Das ist der Dummytext-ViewHelper
	* @param int $length Länge des Dummy-Textes
	*/
	public function render($length = 100) {
		$dummytext = $this->renderChildren();
		$len = strlen($dummytext);
		$repeat = ceil($length / $len);
		$dummytext_neu = substr(str_repeat($dummytext,$repeat),0,$length);
		return $dummytext_neu;
	}
}
?>

Listing 14

In diesem ViewHelper wird nun Folgendes durchgeführt:

  • Es gibt einen Parameter „length“, der (wenn er nicht übermittelt wurde) per Default die Länge 100 enthält.
  • Nun wird der innerhalb des Tags angegebene Dummytext-String so oft wiederholt, bis die angegebene Anzahl an Zeichen erreicht ist. Anschließend wird der String zurückgegeben.
Verwendung des ViewHelpers im Template 

{namespace efempty = Tx_Efempty_ViewHelpers}
<efempty:dummytext length="60">Das ist ein Dummytext! </efempty:dummytext>

Listing 15

Wir haben nun immer ein kleines f: als Namensraum für die ViewHelper verwendet. Dies ist nicht ohne Grund so, sondern wurde implizit definiert. In jedem Fluid-Template kann man nämlich den verwendeten Namensraum mit folgendem Bezeichner definieren: „{namespace f=Tx_Fluid_ViewHelpers}“.

Dadurch wird festgelegt, dass das kleine f als Abkürzung für „Tx_Fluid_ViewHelper“ steht. Damit wird auch klar, warum beispielsweise beim ViewHelper „<f:format>“ die Klasse „Tx_Fluid_ViewHelper_Format“ angesprochen wird. Im obigen Beispiel wurde daher ein eigener Namensraum „efempty“ definiert, der folgerichtig auch als Kürzel vor dem ViewHelper verwendet wird.

Ausblick

Sie haben hier lediglich einen kleinen Einblick in die mächtige Welt der ViewHelper erhalten. Jetzt liegt es an Ihnen, diese Welt zu erkunden, um so das wahre Potenzial der ViewHelper zu entdecken. Ein erster Ansatzpunkt könnte dafür die Extension „ViewHelperTest“ sein [4]. Auch das von Alexander Ebner, Bernhard Ulbrich und dem Autor geschriebene Buch „TYPO3-Extensions: Professionelle Frontend- und Backend-Programmierung“ [5] behandelt neben klassischer Extensionprogrammierung die Themen Extbase und insbesondere Fluid sowie Fluid-ViewHelper ausführlich.

Finde einen Job, den du liebst

Bitte beachte unsere Community-Richtlinien

4 Reaktionen
Manuel

Hi,
sehr guter Artikel, hat mir auf jeden Fall geholfen. Gibt gerade ja nicht übermäßig viel Stoff zu den Viewhelpern.

Davon abgesehen ist mir noch ein "Fehler" aufgefallen, der sicher mit dem Datum des Artikels zu tun hat. Das Fluid-Verzeichnis befindet sich nämlich nicht mehr in „typo3conf/ext/fluid/Classes/ViewHelpers“ sondern in "typo3/sysext/fluid/Classes/Viewhelpers".

So ... weitermachen! ;)

Gruß,
Manuel

Sebastian

Hallo,
ich habe heute auch einen eigenen ViewHelper erstellt. Leider habe ich aber noch 2 Probleme mit ihm:

* Er wird nicht automatisch included, obwohl ich keinen Schreibfehler finde. Ich hab ihn manuell included.
* Wie kann ich innerhalb eines ViewHelpers auf einen anderen, zB den -Helper zugreifen?

Gruss Sebastian

Patrick Lobacher

Hi Christina,

yepp - stimmt. Ich fand den Namen viel schöner als den bisherigen (Schuster) und da dachte ich mir - warum nicht als Mann den Namen der Frau annehmen? Bringt aber massig Probleme mit sich - denn zahlreiche Formulare haben zwar ein Feld für "Mädchenname" aber nahezu keines für "Jungenname" :-)

Patrick

Christina

Ich bin bei einer Recherche auf diesen Artikel gestoßen und muss ehrlich zugeben ich hab ihn mir nicht so wirklich richtig durchgelesen sondern eher überflogen. Ist alles noch recht neu für mich da muss ich erst mal langsam reinkommen. Da sind eben solche Artikel noch ne Nummer zu Groß für mich. Langer Rede kurzer Sinn. Was ich eigentlich los werden wollte. @ Autor Patrick Lobacher hast Du den Namen deiner Frau angenommen? Wenn ja Respekt! Ich weiss auch nicht warum ich das jetzt schreibe ;) Es ist spät ich bin seit 14 Stunden am Arbeiten da kommt man auch mal auf so´n Quatsch.

Liebe Grüße
Gute Nacht
Christina von Markenanwalt

Du musst angemeldet sein, um einen Kommentar schreiben zu können.

Jetzt anmelden