Vorheriger Artikel Nächster Artikel

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

Aus dem
t3n Magazin Nr. 24

06/2011 - 08/2011

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

Fluid-Snippets in Netbeans

Mag. Artikel Heft 24

Bei der Arbeit mit Fluid fallen zwei Dinge sofort auf: Es ist sehr viel 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" ></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.

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.

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.

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.

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.

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:

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.

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:

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 () 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.

Abonniere jetzt t3n-News über WhatsApp und bleib mobil auf dem Laufenden!
t3n-News via WhatsApp!
Vorheriger Artikel Zurück zur Startseite Nächster Artikel
3 Antworten
  1. von Stefan am 22.05.2012 (16:20 Uhr)

    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 Teilen
  2. von Claus am 26.07.2012 (13:15 Uhr)

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

    Antworten Teilen
  3. von sabine.deeken am 05.11.2013 (13:11 Uhr)

    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 Teilen
Deine Meinung

Bitte melde dich an!

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

Jetzt anmelden

Aktuelles aus dem Bereich Fluid
TYPO3 CMS 7.5: Das ist neu
TYPO3 CMS 7.5: Das ist neu

Heute wurde mit TYPO3 CMS 7.5 der letzte Sprint-Release vor dem Long-Term-Support-Release (LTS) veröffentlicht. Die neue Version bringt unter anderem Unterstützung für die einfache Einbindung von … » weiterlesen

TYPO3 CMS 7.3 ist da: Das bringt die neue Version
TYPO3 CMS 7.3 ist da: Das bringt die neue Version

Heute wurde mit TYPO3 CMS 7.3 der nächste Sprint-Release auf dem Weg zum Long-Term-Support (LTS) veröffentlicht, der unter anderem eine verbesserte Performance und eine bessere Stabilität mitbringt. » weiterlesen

TYPO3 CMS, WordPress, Joomla! und Co.: Die wichtigsten Updates für die wichtigsten CMS (September)
TYPO3 CMS, WordPress, Joomla! und Co.: Die wichtigsten Updates für die wichtigsten CMS (September)

Bei den großen CMS und einem Blog-System gibt es regelmäßig Updates, die neue Funktionen liefern oder auch „nur“ Sicherheitslücken schließen. Die aus dem Monat September stellen wir euch … » weiterlesen

Alle Hefte Jetzt abonnieren – für nur 35 €

Kennst Du schon unser t3n Magazin?