Anzeige
Anzeige
UX & Design
Artikel merken

TYPO3: Zehn Tipps & Tricks für Extbase und Fluid

Immer mehr TYPO3-Entwickler setzen auf die Kombination aus Extbase (MVC) und Fluid (Templating). Wer von dieser Art zu programmieren infiziert ist, kommt so schnell nicht mehr davon los. Wir leisten mit zehn Praxistipps Entwicklungshilfe.

9 Min. Lesezeit
Anzeige
Anzeige

Fluid-Snippets in Netbeans

Mag. Artikel Heft 24

Anzeige
Anzeige

Bei der Arbeit mit Fluid fallen zwei Dinge sofort auf: Es ist sehr viel Code nötig, und durch die zunächst ungewöhnliche Notation entstehen immer wieder Fehler, die man schwer findet. Da wäre eine Art Snippet-Verzeichnis praktisch, mit dem man oft benötigte Code-Fragmente auf Knopfdruck einfügen kann. Zumindest für die beliebte IDE Netbeans [1] gibt es nun etwas Derartiges – so genannte Code Templates [2]. Eine überarbeitete und erweiterte Version der Datei findet sich außerdem bei Typovision [3].

Code Code-Fragment
alias <f:alias map=“{key:value}“>
base <f:base />
ts bzw. cobject <f:cObject typoscriptObjectPath=“path“></f:cObject>
count <f:count subject=“{objects}“ />
cycle <f:cycle values=“{array}“ as=“value“></f:cycle>
debug <f:debug title=“debugname“>{debug_var}</f:debug>
esc bzw. escape <f:escape>text</f:escape>
flash <f:renderFlashMessages />
for <f:for each=“{array}“ as=“value“ key=“key“></f:for>
crop <f:format.crop maxCharacters=“15″ append=“&nbsp;[…]“></f:format.crop>
currency <f:format.currency currencySign=“sign“ decimalSeparator=“dsep“ thousandsSeparator=“tsep“></f:format.currency>
date <f:format.date format=“d.m.Y-H:i:s“></f:format.date>
html <f:format.html parseFuncTSPath=“lib.parseFunc“></f:format.html>
nl2br <f:format.nl2br></f:format.nl2br>
number <f:format.number decimals=“decimals“ decimalSeparator=“dsep“ thousandsSeparator=“tsep“></f:format.number>
padding <f:format.padding padLength=“length“ padString=“string“></f:format.padding>
printf <f:format.printf arguments=“arguments“></f:format.printf>
form <f:form action=“action“ controller=“controller“ object=“{object}“ name=“name“ arguments=“arguments“ ></f:form>
checkbox <f:form.checkbox property=“property“ checked=“{condition}“ />
errors <f:form.errors for=“for“>{error.propertyName} {error.message} {error.code}</f:form.errors>
hidden <f:form.hidden name=“name“ value=“value“ />
password <f:form.password property=“property“ />
radio <f:form.radio property=“property“ checked=“{condition}“ />
select <f:form.select property=“property“ options=“{key:value}“
optionLabelField=“labelfield“
optionValueField=“valuefield“ />
submit <f:form.submit value=“value“ />
textarea <f:form.textarea property=“property“ rows=“rows“ cols=“cols“ />
textfield bzw. input <f:form.textfield property=“property“ />
upload <f:form.upload property=“property“ />
group <f:groupedFor each=“{array}“ as=“items“ groupBy=“group_by“ groupKey=“group_key“>{group_key}:<f:for each=“{items}“ as=“value“ key=“key“></f:for>
</f:groupedFor>
if <f:if condition=“{condition}“></f:if>
ife <f:if condition=“{condition}“> <f:then></f:then><f:else></f:else></f:if>
img bzw. image <f:image src=“source“ alt=“alt“ />
layout <f:layout name=“name“ />
section <f:section name=“name“></f:section>
render <f:render partial=“name“ arguments=“arguments“ />
link <f:link.action action=“action“ controller=“controller“></f:link.action>
linka <f:link.action action=“action“ controller=“controller“ arguments=“{key:value}“></f:link.action>
extlink <f:link.external uri=“url“ target=“_blank“></f:link.external>
email <f:link.email email=“email“ />
page <f:link.page pageUid=“uid“></f:link.page>
securityifa <f:security.ifAuthenticated“><f:then></f:then></f:security.ifAuthenticated“>
securityifh <f:security.ifHasRole“ role=“role“> <f:then></f:then></f:security.ifHasRole“>
trans bzw. translate <f:translate key=“key“ />

Nach dem Download lassen sich die Templates in Netbeans über „Einstellungen, Import“ laden. In den Import-Optionen reicht ein „All“ und „OK“. Nach einem Neustart stehen die Code-Templates zur Verfügung. Um nun ein Code-Template zu verwenden, reicht in einer Fluid-Datei (Typ: HTML) ein Code-Schnipsel aus der abgedruckten Tabelle. Ein anschließender Klick auf Tab fügt das zugehörige Fragment ein.

Anzeige
Anzeige

String-Vergleiche in Fluid

Im If-ViewHelper von Fluid kann man über die Eigenschaft „condition“ nahezu alles vergleichen – Zahlen (kleiner, größer, gleich), Booleans (TRUE, FALSE) etc. – nicht jedoch auf Gleichheit mit einem String. Einem Bugtracker-Eintrag zufolge sei das schwierig zu implementieren.

Anzeige
Anzeige

Aber gerade eine solche Abfrage braucht man in der Praxis relativ oft. Man kann nun anfangen, über den Alias-ViewHelper eben einen Alias für den abzufragenden String zu erstellen und gegen diesen zu prüfen. Das bringt allerdings zwei Nachteile mit sich: Für jeden abzufragenden String müsste der in Frage kommende Programmteil mit einem Alias-ViewHelper ummantelt werden. Außerdem wird der Code unübersichtlich, da an der Stelle der Überprüfung nun nicht mehr der Prüfstring selbst, sondern der Alias verwendet wird – beides also suboptimal. Auch das Schreiben eines eigenen ViewHelper scheint nicht adäquat.

Aber es geht auch einfacher: Die beiden Operatoren müssen dazu lediglich in ein Array verwandelt werden und schon funktioniert der Vergleich wie gewünscht.

Anzeige
Anzeige
String-Vergleich in Fluid
<f:if condition="{0:blog.title} == {0:'Testtitel'}">
	Funktioniert: Der Titel lautet 'Testtitel'.
</f:if>

Listing 1

Kommentare im ViewHelper

Um im anfänglichen Code-Gewusel der Views den Überblick nicht zu verlieren, eignen sich Kommentare zur Strukturierung. Da man sich aber letztlich in HTML befindet, ist es eben nur möglich, HTML-Kommentare zu verwenden. Diese allerdings sind im Quellcode selbst sichtbar.

Eine andere Lösung muss also her. Hier bietet es sich an, einen eigenen ViewHelper zu erstellen, der nichts anderes macht, als Kommentare nicht anzuzeigen: Nach der Installation der Extension „efempty“ – diese enthält ein Extbase/Fluid-Grundgerüst – wird der ViewHelper um eine Klasse erweitert. Im Verzeichnis Classes/ViewHelpers/ genügt dafür eine Datei CommentViewHelper.php mit folgendem Inhalt:

CommentViewHelper.php
<?php
class Tx_Efempty_ViewHelpers_CommentViewHelper extends Tx_Fluid_Core_ViewHelper_AbstractViewHelper {
	public function render() {
		return '';
	}
}
?>

Listing 2

Anschließend reicht überall dort, wo der ViewHelper verwendet wird, eine Namespace-Anweisung am Anfang der Datei.

Anzeige
Anzeige
Namespace im View
{namespace tv = Tx_Efempty_ViewHelpers}
...
<tv:comment> Hier können Sie nun beliebig kommentieren. Sie können sogar andere ViewHelper auskommentieren! :-)</tv:comment>

Listing 3

Übersetzte Fehlermeldungen

Wer mittels „<f:form.errors>{error.propertyName} {error.message} {error.code}</f:form.errors>“ Fehlermeldungen ausgibt, wird schnell feststellen, dass diese lediglich in englischer Sprache erscheinen. Übersetzen lassen sich die Ausgaben nicht, denn bislang sind sie hart kodiert (dies soll sich zukünftig ändern).

Um dieses Problem zu lösen, ist etwas Fantasie nötig: Für jeden Fehler gibt es einen eindeutigen Code. Dieser besteht laut Vereinbarung aus dem Timestamp, an dem der Fehler „erfunden“ wurde. Daher kann davon ausgegangen werden, dass es in einem System nur einen Fehler mit eben dieser Meldung gibt. Somit kann über den Translate-ViewHelper und den Code als Schlüssel die Fehlermeldung übersetzt werden.

Resources/Private/Language/locallang.xml
<languageKey index="de" type="array">
	<label index="1221560718">Der Wert ist leer!</label>
</languageKey>

Listing 4

Im View
<f:translate key="{error.code}" />

Listing 5

Default-Sortierung im Repository

Nicht unbedingt unbekannt, aber weitestgehend ungenutzt ist ein Feature, welches in Extbase 1.3 hinzugekommen ist: Im Repository besteht die Möglichkeit, das Ergebnis nach bestimmten Feldern aufsteigend oder absteigend zu sortieren. Dies wird beispielsweise mittels „$query->setOrderings(array(’name‘ => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING));“ erledigt.

Anzeige
Anzeige

Um dies nicht für jede einzelne Repository-Funktion einstellen zu müssen, gibt es die Eigenschaft defaultOrderings:

Im Repository
protected $defaultOrderings = array ('name' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING);

Listing 6

Eigenes Template im Controller

Normalerweise ist in Extbase und Fluid immer alles genau geregelt – „Convention over Configuration“ lautet das Motto und das macht durchaus Sinn. So ist beispielsweise festgelegt, dass im Controller automatisch der View (und damit zunächst die HTML-Datei) ermittelt wird, der zum Controller und zur Action passt. Eine List-Action des Blog-Controllers (List.html) erwartet das Template somit im Verzeichnis Resources/Private/Templates/Blog/.

Nun kann es aber durchaus sein, dass man von dieser Vorgabe abweichen und ein eigenes Template definieren möchte – beispielsweise damit ein eingeloggter Administrator ein anderes Template erhält als der normale User. Dies kann über die Methode setTemplatePathAndFilename wie folgt realisiert werden:

Anzeige
Anzeige
Im Controller – innerhalb einer Action
public function listAction() {
	...
	$this->view->setTemplatePathAndFilename(
			'typo3conf/ext/' .
			$this->request->getControllerExtensionKey() .
			'/Resources/Private/Templates/Blog/NeuesTemplate.html'
		);
	...
	$this->view->assign(...);
}

Listing 7

Individuelle SQL-Abfragen

Extbase besitzt einen sehr leistungsfähigen Query-Manager. Dieser baut Abfragen so zusammen, wie es SQL eben passt – getreu dem Motto „Don’t care about databases“. Tatsächlich gibt es im Domain Driven Design (der Philosophie, die hinter FLOW3 und damit auch hinter Extbase steht) keine Datenbanken, sondern nur „Repositories“.

Damit wird die technische Realisierung der Speicherung letztlich außerhalb der Domäne transportiert. Folgerichtig befindet sich in der Domäne kein SQL – denn dies funktioniert ja nur mit einer Datenbank. Die Daten könnten aber auch im Filesystem oder in einem Webservice gespeichert sein.

Im Alltag wird aber nach wie vor eine MySQL-Datenbank verwendet werden und so existiert auch in Extbase eine Möglichkeit, Datenbanken direkt anzusprechen. Dies kann zum Beispiel dann von Nutzen sein, wenn komplexe Queries für statistische Auswertungen oder auf Performance optimierte Befehle abgesetzt werden sollen.

Anzeige
Anzeige

Damit dies allerdings gelingt, muss der interne Mechanismus ausgeschaltet werden, mit dem Extbase probiert, aus dem Query-Result wieder fertige Objekte zu konstruieren. Hier kommen die so genannten Query-Settings und mit ihnen die Statement-Methode der Query-Factory ins Spiel:

Im Repository
public function findRaw(){
	$query = $this->createQuery();
	$query->getQuerySettings()->setReturnRawQueryResult(TRUE);
	$query->statement('SELECT * from tx_simpleblog_domain_model_blog');
	return $query->execute();
}

Listing 8

Es wird nun statt des Objekt-Interfaces ein Array mit dem Ergebnis der Datenbankabfrage zurückgeliefert. Allerdings verliert Fluid damit seine Aufwärtskompatibilität zu FLOW3. Nun noch Controller und View angepasst und schon steht dem Datenbankzugriff nichts mehr im Weg:

Controller
$this->view->assign('result',$this->blogRepository->findRaw());

Listing 9

View
<f:for each="{result}" as="row">
	{row.title}<br />
</f:for>

Listing 10

Settings-Zugriff

Extbase macht es einem wirklich leicht. Unter anderem kann direkt auf TypoScript/Flexform-Settings zugreifen, wer sich an ein paar einfache Regeln hält:

Anzeige
Anzeige
TypoScript
plugin.tx_[lowercasedextensionname] {
	settings {
		setting1 = wert1
	}
}

Listing 11

Flexform
<T3DataStructure>
...
	<el>
		<settings.setting1>
		...
		</settings.setting1>
	</el>
...
</T3DataStructure>

Listing 12

So lässt sich über {settings.setting1} im View beziehungsweise $this->settings[’setting1′] im Controller auf die Werte zugreifen. Was aber, wenn im Repository, im ViewHelper oder im Validator diese Settings gebraucht werden? Wirklich elegant geht dies nur im Repository, wenn der Controller aus der Repository-Methode das Setting direkt als Parameter mitgibt. Alternativ (wenn auch nicht offiziell) klappt es auch über den Configuration-Manager und der seit Extbase 1.3 hinzugefügten Dependency Injection:

Im ViewHelper beispielsweise
class Tx_Efempty_ViewHelpers_TestViewHelper extends Tx_Fluid_Core_ViewHelper_AbstractViewHelper {
	protected $configurationManager;
	public function injectConfigurationManager(Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager) {
		$this->configurationManager = $configurationManager;
	}
	public function render() {
		// Hier werden nun die Settings ermittelt.
		// Der erste Parameter von getConfiguration muss 'Settings' lauten.
		// Der zweite Parameter beinhaltet den Namen der Extension.
		// Der dritte Parameter beinhaltet den Namen des Plugins.
		$settings = $this->configurationManager->getConfiguration('Settings','Efempty','Pi1');
		...
	}
}

Listing 13

GET/POST Vars

Wenn schon mit Extbase und Fluid programmieren, dann auch ausschließlich. Methoden der „klassischen“ API – also etwa t3lib_div::_GP(‚myGPVar‘) – sollten tabu sein. Wie aber werden dann die GET- beziehungsweise POST-Variablen genutzt?

Auch das ist im Grunde sehr einfach: Die Variable muss einfach automatisch zur Verfügung stehen. Dafür muss schlicht der Namespace passen. Dieser wird automatisch hinzugefügt, wenn beispielsweise ein Feld in einem Formular mit dem entsprechenden ViewHelper anlegt wird. Da keine Zuordnung zum Model benötigt wird, kann auch auf den Property-Parameter verzichtet werden. Im Controller kann nun die Variable über die Annotation registriert und innerhalb der Methode darauf zugegriffen werden. Das Beispiel zeigt ein Suchformular:

View und Controller
<f:form method="post" action="index">
	<f:form.textfield value="{search}" name="search" />
	<f:form.submit value="Suchen" />
</f:form>
...
/**
 * @param string $search
 */
	public function indexAction($search='*') {
		$this->view->assign('search', $search);

Listing 14

Der Zugriff auf die GET/POST-Variablen kann zudem über $this->request->hasArgument($var) und $this->request->getArgument($var) erfolgen.

Migration zu FLOW3

Wer nur die Boardmittel von Extbase und Fluid verwendet (statt „alter“ TYPO3-API-Befehle), der kann seine Extbase-Extension auch unter FLOW3 verwenden – so die Theorie. In der Praxis funktioniert das aber nur unter folgender Anleitung.

Ausgegangen wird von der Extension „efempty“, die zunächst aus dem TER installiert werden muss. Zusätzlich wird Phoenix Sprint Release 6 (oder FLOW3 Alpha 14 bzw. neuer) installiert – eine Anleitung hierfür bieten diverse Blogs [4].

Jetzt geht es ans Kopieren: Das Verzeichnis typo3conf/ext/efempty (TYPO3) in das Verzeichnis Packages/Application/Efempty (FLOW3, großer Anfangsbuchstabe). Sicherlich wird es in absehbarer Zeit Migrationsskripte geben – aber bis es soweit ist, müssen einige Änderungen manuell durchgeführt werden.

Classes/Controller/StartController.php
<?php
declare(ENCODING = 'utf-8');
namespace F3\Efempty\Controller;
...
class StartController extends \F3\FLOW3\MVC\Controller\ActionController {
...
// Instanzierung
$start = new \F3\Efempty\Domain\Model\Start;

Listing 15

Allen Klassen (also allen Dateien im Classes-Verzeichnis) müssen Namespace-Deklarationen vorangestellt werden. Diese beginnen (hier) immer mit F3 und darauf folgen alle Verzeichnisse mit einem „\“ getrennt. Dafür können anschließend alle Klassennamen verkürzt werden auf den eigentlichen Namen. Bei den abgeleiteten Klassen wiederum muss der Pfad mit angegeben werden – anders als in Extbase ebenfalls mit F3 beginnend und die Pfadbestandteile mit „\“ getrennt.

Bei der Domain-Model-Klasse kann sogar ganz auf die Erweiterung der abstrakten Klasse verzichtet werden, hier reicht die entsprechende Annotation (@entity bzw. @valueobject).

Classes/Domain/Model/Start.php
<?php
declare(ENCODING = 'utf-8');
namespace F3\Efempty\Domain\Model;
...
// @entity
class StartController extends \F3\FLOW3\MVC\Controller\ActionController {
...
}

Listing 16

Nun fehlt noch eine Route für die Extension in der Datei Configuration/Routes.yaml:

Configuration/Routes.yaml
name: 'Efempty'
uriPattern: 'efempty'
defaults:
	'@package': 'Efempty'
	'@controller': 'Start'
	'@action': 'index'
	'@format': 'html'

Listing 17

Abschließend muss die (nun Package genannte) Extension im System aktiviert werden. Im besten Fall funktioniert das mit dem Kommandozeilentool von FLOW3; alternativ reicht ein entsprechender Eintrag in der Datei Configuration/PackageStates.yaml.

Package-Aktivierung
./flow3 package activate Efempty

Listing 18

Bei einem Aufruf via http://IhreDomain/efempty zeigt sich, dass die Extension (Package) jetzt auch unter FLOW3 läuft.

Mehr zu diesem Thema
Fast fertig!

Bitte klicke auf den Link in der Bestätigungsmail, um deine Anmeldung abzuschließen.

Du willst noch weitere Infos zum Newsletter? Jetzt mehr erfahren

Anzeige
Anzeige
3 Kommentare
Bitte beachte unsere Community-Richtlinien

Wir freuen uns über kontroverse Diskussionen, die gerne auch mal hitzig geführt werden dürfen. Beleidigende, grob anstößige, rassistische und strafrechtlich relevante Äußerungen und Beiträge tolerieren wir nicht. Bitte achte darauf, dass du keine Texte veröffentlichst, für die du keine ausdrückliche Erlaubnis des Urhebers hast. Ebenfalls nicht erlaubt ist der Missbrauch der Webangebote unter t3n.de als Werbeplattform. Die Nennung von Produktnamen, Herstellern, Dienstleistern und Websites ist nur dann zulässig, wenn damit nicht vorrangig der Zweck der Werbung verfolgt wird. Wir behalten uns vor, Beiträge, die diese Regeln verletzen, zu löschen und Accounts zeitweilig oder auf Dauer zu sperren.

Trotz all dieser notwendigen Regeln: Diskutiere kontrovers, sage anderen deine Meinung, trage mit weiterführenden Informationen zum Wissensaustausch bei, aber bleibe dabei fair und respektiere die Meinung anderer. Wir wünschen Dir viel Spaß mit den Webangeboten von t3n und freuen uns auf spannende Beiträge.

Dein t3n-Team

Stefan

Schöne Übersicht, ein paar Punkte sind sehr hilfreich bei Problemen die bisher echt Nerven gekostet haben. Die verfügbaren Extbase/Fluid-Dokus sind ja insgesamt etwas mager und wiederholen sich auf einem bestimmten Niveau.
Hier sind aber endlich mal ein paar neue Beispiele – vielen Dank!

Antworten
Claus

Das Code Template von Typovision enthält einen Fehler. Das Image Tag hat kein Argument source, sondern src.

Antworten
sabine.deeken

Settings-Zugriff im Viewhelper mit Namespaces (getestet in TYPO3 6.1):

/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
* @inject
*/
protected $configurationManager;

/**
* Injects the configuration manager, retrieves the plugin settings from it
*
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
* @return void
* @override \TYPO3\CMS\Extbase\Mvc\Controller\AbstractController
**/
public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager) {
$this->configurationManager = $configurationManager;
$settings = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS);
$this->settings = $settings;
}

Antworten

Melde dich mit deinem t3n Account an oder fülle die unteren Felder aus.

Bitte schalte deinen Adblocker für t3n.de aus!
Hallo und herzlich willkommen bei t3n!

Bitte schalte deinen Adblocker für t3n.de aus, um diesen Artikel zu lesen.

Wir sind ein unabhängiger Publisher mit einem Team von mehr als 75 fantastischen Menschen, aber ohne riesigen Konzern im Rücken. Banner und ähnliche Werbemittel sind für unsere Finanzierung sehr wichtig.

Schon jetzt und im Namen der gesamten t3n-Crew: vielen Dank für deine Unterstützung! 🙌

Deine t3n-Crew

Anleitung zur Deaktivierung
Artikel merken

Bitte melde dich an, um diesen Artikel in deiner persönlichen Merkliste auf t3n zu speichern.

Jetzt registrieren und merken

Du hast schon einen t3n-Account? Hier anmelden

oder
Auf Mastodon teilen

Gib die URL deiner Mastodon-Instanz ein, um den Artikel zu teilen.

Anzeige
Anzeige