TYPO3-Extensions: Professionelle Frontend- und Backend-Programmierung

alexander EBNER patrick LOBACHER bernhard ULBRICH TYPO3 EXTENSIONS PROFESSIONELLE FRONTEND- UND BACKENDPROGRAMMIERUNG ...

357 downloads 1902 Views 3MB Size Report

This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!

Report copyright / DMCA form

'; $c++; } // Anzahl der Kommentare ermitteln und zuweisen. $count = $c-1; $marker['###COMMENTCOUNT###'] = $cObj->stdWrap('<span id="tx_elublog_commentcountnumber">'.$count.''.$this-> pi_getLL('comments'),$this->conf['singleView.']['comment.'] ['commentcount_stdWrap.']); $content = ($contentItem != '') ? $this->cObj-> substituteMarkerArrayCached($template['comment'], $marker, array('###COMMENTITEM###' => $contentItem)):$this->pi_getLL ("noComments"); return 'pi_classParam('comments').'>'.$content.'
'; }

Es wird eine Abfrage gegen die Datenbank abgesetzt, die alle Kommentare aus der Tabelle tx_elublog_blogcomments sucht, die dem Beitrag zugeordnet sind. Die Ergebnisse werden durchlaufen und den Markern zugeordnet.

116

4.4 Die Kommentarfunktion mit AJAX und eID Listing 4.36 HTML-Template für die Kommentare ###COMMENTCOUNT### ###COMMENTNUMBER### ###COMMENTAUTHOR### ###AT### ###COMMENTDATE### ###GRAVATAR### ###COMMENTTEXT###


Der Container mit ID tx_elublog_newcomment wird den neuen Kommentar anzeigen, den der Besucher hinterlässt. Eine Besonderheit ist der Marker ###GRAVATAR###. Ihm wird ein sogenannter Gravatar zugeordnet. Das ist ein Bild, auch als Avatar bekannt, das oft den Kommentarverfasser, aber auch beliebig andere Inhalte zeigt. Das Bild ist einer Mail-Adresse zugeordnet und wird unter http://www.gravatar.com verwaltet. Zur Zeit der Drucklegung ist dieser Dienst kostenlos (und wird es wohl auch bleiben). Die Gravatar-Bilder haben in der Regel eine Größe zwischen 40 und 120 Pixel. Über eine API-Schnittstelle wird der MD5-Hash der Mail-Adresse bei Gravatar.com abgefragt, ob dafür ein Bild existiert. Wenn ja, wird dieses in der angeforderten Größe zurückgesendet. Andernfalls wird ein Standardbild geliefert. Zwei Parameter müssen über TypoScript konfiguriert werden: Listing 4.37 Gravatar-Konfiguration // Größe des Gravatars in Pixel plugin.tx_elublog_pi1.singleView.comment.gravatar_size = 40 // URL des Standardbildes plugin.tx_elublog_pi1.singleView.comment.gravatar_default_url = http://example.com/fileadmin/elublog/gravatar.png

Diese Parameter werden an die API zusammen mit dem MD5-Hash der Mail-Adresse übermittelt. Das Ergebnis wird in ein -Tag eingebaut.

Abbildung 4.7 Die Ausgabe der Kommentare mit Gravataren

117

4 Frontend-Plug-ins

4.4.2

Das Kommentarformular hinzufügen

Auch für das Formular haben wir bereits eine Funktion vorbereitet. Diese heißt $this-> getCommentForm(). Das Formular selbst bauen wir als HTML-Vorlage auf. Darin werden

nur ein paar Labels zum Substituieren sein. Listing 4.38 $this->getCommentForm() function getCommentForm() { /* ###### Platzhalter für AJAX-Funktionalität ###### */ $markerArray['###LABEL_COMMENTFORM###'] = $this-> pi_getLL('label_commentform'); $markerArray['###LABEL_COMMENTFORM_AUTHOR###'] = $this-> pi_getLL('label_commentform_author'); $markerArray['###LABEL_COMMENTFORM_AUTHOREMAIL###'] = $this-> pi_getLL('label_commentform_authoremail'); $markerArray['###LABEL_COMMENTFORM_WEBSITE###'] = $this-> pi_getLL('label_commentform_website'); $markerArray['###LABEL_COMMENTFORM_COMMENT###'] = $this-> pi_getLL('label_commentform_comment'); $markerArray['###LABEL_COMMENTFORM_SUBMIT###'] = $this-> pi_getLL('label_commentform_submit'); $markerArray['###BID###'] = $this->internal['currentRow']['bid']; $markerArray['###BLOGENTRY###'] = $this->internal['currentRow']['uid']; $markerArray['###DATEFORMAT###'] = $this->conf['singleView.'] ['comment.']['dateFormat']; $template['commentform'] = $this->cObj->getSubpart($this-> templateFile,'###COMMENTFORMSET###'); $content = $this->cObj->substituteMarkerArrayCached ($template['commentform'],$markerArray); return $content; }

In den Platzhalter am Anfang der Funktion setzen wir gleich die AJAX-Funktionalität ein. Die Übersetzung der Labels kommt wieder aus locallang.xml. Listing 4.39 locallang.xml index="label_commentform_author">Name(*): index="label_commentform_authoremail">E-Mail(*): index="label_commentform_website">URL index="label_commentform_comment">Kommentar index="label_commentform_submit">Senden

Das Formular selbst ist unspektakulär. Es enthält die erforderlichen Felder und Labels. Die einzige Besonderheit ist die Abwesenheit eines
-Tags. Das Formular wird mit einem onclick-Event getriggert. Listing 4.40 HTML-Template-Vorlage
###LABEL_COMMENTFORM###


118

4.4 Die Kommentarfunktion mit AJAX und eID





Nun fehlt allerdings noch die komplette AJAX-Funktionalität. Im Prinzip wollen wir die Eingaben des Formulars zum Server übertragen, wo sie in die Datenbank geschrieben werden. Quasi als Bestätigung, sollen die Daten zurück zum Skript geschickt und als neuer Kommentar dargestellt werden. Ab Version 4.3 wird TYPO3 mit dem ExtJS JavaScript-Framework ausgeliefert. ExtJS ist ein sehr mächtiges Framework, das wir für unsere Zwecke einsetzen möchten. Wir nutzen es für den AJAX-Request selbst, um dafür den zurückgelieferten Kommentar unter den bestehenden anzufügen. Der Vorteil, wie wir gleich sehen werden, ist der, dass wir dazu wieder das Template aus der Template-Vorlage verwenden können. Natürlich kann das Beispiel auch mit anderen Frameworks oder sogar nur mit eigenem JavaScript-Code umgesetzt werden. Fügen Sie Listing 4.41 und Listing 4.42 in den Platzhalter aus Listing 4.38 ein. Da wir die Template-Vorlage, die schon besteht, verwenden wollen, müssen wir diese laden und die Marker substituieren. Da die Inhalte aber erst auf dem Client nach dem AJAXRequest bereitstehen, substituieren wir sie wiederum mit eigenen Markern für ExtJS. Listing 4.41 Template-Vorlage für ExtJS erstellen $cObj = t3lib_div::makeInstance('tslib_cObj'); $gravatar_size = $this->conf['singleView.']['comment.'] ['gravatar_size']; $gravatar_default = $this->conf['singleView.']['comment.'] ['gravatar_default_url']; $template['comment'] = $this->cObj->getSubpart($this-> templateFile,'###COMMENT###'); $template['item'] = $this->cObj->getSubpart($this-> templateFile,'###COMMENTITEM###'); $fixedMarkerArray['###AT###'] = $this->pi_getLL('at'); $markerArray['###COMMENTNUMBER###'] = $cObj-> stdWrap('{commentnumber}',$this->conf['singleView.']['comment.']

119

4 Frontend-Plug-ins ['number_stdWrap.']); $markerArray['###COMMENTAUTHOR###'] = $cObj->stdWrap('{commentauthor}', $this->conf['singleView.']['comment.']['author_stdWrap.']); $markerArray['###COMMENTTEXT###'] = $cObj->stdWrap('{comment}',$this-> conf['singleView.']['comment.']['comment_stdWrap.']); $markerArray['###COMMENTDATE###'] = $cObj->stdWrap('{commentdate}', $this->conf['singleView.']['comment.']['crdate_stdWrap.']); $markerArray['###GRAVATAR###'] = $cObj->stdWrap('{commentemail}', $this->conf['singleView.']['comment.']['gravatar_stdWrap.']); $markerArrayMerged = array_merge($markerArray,$fixedMarkerArray); $html = str_replace("\n",'','pi_classParam('comment').'>' .$this->cObj->substituteMarkerArrayCached($template['item'], $markerArrayMerged).'
');

Wir lesen die Einstellungen für die Gravatar-Darstellung ein. Dann geht es an das Substituieren. Am Beispiel ###COMMENT### sieht man, dass der Marker durch {comment} ersetzt wird, nachdem ein stdWrap darauf angewendet wurde. Auch ist ersichtlich, dass der Subpart ###COMMENT### nicht verwendet wird, sondern nur der tiefer liegende ###ITEM###. Listing 4.42 Das JavaScript $js = "<script type=\"text/javascript\">/*".$this->pi_getLL('comment_status_send'). "'); // Absetzen des AJAX-Requests Ext.Ajax.request({ method: 'POST', url: 'index.php?eID=tx_elublog_eid', params: { author : Ext.get('tx_elublog_commentform_author').dom.value, authoremail : Ext.get('tx_elublog_commentform_authoremail').dom.value, website : Ext.get('tx_elublog_commentform_website').dom.value, comment : Ext.get('tx_elublog_commentform_comment').dom.value, bid : Ext.get('tx_elublog_commentform_bid').dom.value, blogentry : Ext.get('tx_elublog_commentform_blogentry').dom.value, dateformat : Ext.get('tx_elublog_commentform_dateformat').dom.value }, success: function(xhr,params) { var json = eval('('+xhr.responseText+')'); if (json.fail == 0) { Ext.get('tx_elublog_comment_status').update('<span class=\"comment_update_success\">".$this->pi_getLL ('comment_status_success')."'); // Einlesen des Templates var html = '".$html."'; var tpl = new Ext.Template(html); tpl.compile(); // Die zurückgegebenen Daten werden in das Template gepackt und damit in den div-Container tx_elublog_newcomment eingefügt. tpl.append('tx_elublog_newcomment', { commentauthor: json.author, commentemail: '', commentdate: json.crdate, comment: json.comment, commentnumber: json.commentnumber }); Ext.get('tx_elublog_commentcountnumber').update (json.commentnumber);

120

4.4 Die Kommentarfunktion mit AJAX und eID Ext.get('tx_elublog_commentform_author').dom.value = ''; Ext.get('tx_elublog_commentform_authoremail').dom.value = ''; Ext.get('tx_elublog_commentform_website').dom.value = ''; Ext.get('tx_elublog_commentform_comment').dom.value = ''; } else { Ext.get('tx_elublog_comment_status').update('<span class=\"comment_update_fail\">".$this->pi_getLL ('comment_status_dbfail')."'); } }, failure: function(xhr,params) { Ext.get('tx_elublog_comment_status').update('<span class=\"comment_update_fail\">".$this->pi_getLL ('comment_status_fail')."'); }}) }); });// --> ";

Da JavaScript nur am Rande mit der Extension-Programmierung zu tun hat, gehen wir auf das Skript nur kurz ein. Die Funktion Ext.Ajax.request() löst einen AJAX-Request mit den übergebenen Parametern aus. Der Parameter params enthält ein Array mit den zu übergebenden Parameter an das Zielskript. Diese werden aus den Values der Input-Felder ausgelesen. Im Erfolgsfall wird das Template, das zuvor erzeugt und in die Variable $html geschrieben wurde, mit den Rückgabeparametern befüllt und in den Div-Container tx_elublog_ newcomment eingehängt. Im Fehlerfall wird die Statusmeldung mit einem Fehler ausgegeben. Der AJAX-Request wird an die URL index.php?eID=tx_elublog_eid gesendet. Der Parameter eID stellt eine Besonderheit dar. Ist er gesetzt, wird der Frontend-RenderingProzess abgebrochen und die Klasse tslib_eidtools eingebunden. eID-Skripte sind etwas schwieriger zu programmieren, da viele Klassen nicht zur Verfügung stehen, dazu gehören zum Beispiel auch die tslib_pibase und tslib_cObj. Da wir aber nur den Zugriff auf die Datenbank und den FE-User benötigen, stellt das kein Problem dar. Neu in TYPO3 4.3

Wie gesagt, stehen in eID-Skripten nicht alle Funktionen zur Verfügung. Mit „FOUR3“ kommen aber ein paar Änderungen: Mit tslib_eidtools::initLanguage() wird die Sprachunterstützung bereitgestellt. Mit tslib_eidtools::initTCA() wird das TCA geladen. Und mit tslib_eidtools::getTSFE() wird die Frontend-Klasse geladen. Somit können diese Funktionalitäten ebenfalls in eID-Skripten verwendet werden.

Listing 4.43 Die eID-Datei
121

4 Frontend-Plug-ins // Verbindung zur Datenbank aufbauen. tslib_eidtools::connectDB(); class tx_elublog_eID { function main() { // Extension-Konfiguration einlesen $this->extconf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['elublog']); // Aufbauen des Daten-Arrays für den Datenbank-INSERT. $fields = array ( 'author' => trim(t3lib_div::_GP('author')), 'authoremail' => trim(t3lib_div::_GP('authoremail')), 'website' => trim(t3lib_div::_GP('website')), 'comment' => trim(t3lib_div::_GP('comment')), 'blogentry' => intval(t3lib_div::_GP('blogentry')), 'bid' => intval(t3lib_div::_GP('bid')), 'crdate' => time(), 'tstamp' => time(), 'cruser_id' => $GLOBALS['TSFE']->fe_user->user['uid'], 'pid' => $this->extconf['storageId'] ); // Daten in die Datenbank schreiben if ($res = $GLOBALS['TYPO3_DB']->exec_INSERTquery ('tx_elublog_blogcomments',$fields)) { // Im Erfolgsfall eine Datenbankabfrage erstellen $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'tx_elublog_blogcomments', "hidden = 0 AND deleted = 0 AND blogentry=".intval(t3lib_div::_GP('blogentry'))); // sql_num_rows() gibt die Anzahl der Datensätze zu der Datenbankabfrage zurück. Das Array $fields, das die Werte enthält, welche in die Datenbank geschrieben wurden, wird erweitert, bzw. es werden Werte nachbearbeitet. $fields['commentnumber'] = $GLOBALS['TYPO3_DB']-> sql_num_rows($res); $fields['fail'] = 0; $fields['authoremail'] = md5(strtolower(trim(t3lib_div::_GP('authoremail')))); $fields['crdate'] = date(t3lib_div::_GP('dateformat'),time()); // Das Array wird als JSON-String kodiert und ausgegeben echo json_encode($fields); } else { // Schlägt das Schreiben in die Datenbank fehl, wird ein Fehlerflag gesendet, das vom JavaScript ausgewertet wird. echo json_encode(array('fail' => 1)); } } } // XCLASS-Deklaration if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/elublog/res/class.tx_elublog_eid .php']) { include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/elublog/res/class.tx_ elublog_eid.php']); } // Instanz der Klasse tx_elublog_eid erzeugen und die Funktion main() aufrufen, um das Skript auszuführen. $SOBE = t3lib_div::makeInstance('tx_elublog_eID'); $SOBE->main(); ?>

Als Ergebnis liefert das Skript einen JSON-String zurück, der von JavaScript mittels eval() in ein Array zurückverwandelt und ausgegeben wird. Da eID-Skripte nicht den ganzen Rendering-Prozess durchlaufen, und nur ein ganz kleiner Teil des TYPO3-Frameworks geladen wird, sind sie bedeutend schneller und ressourcen-

122

4.5 Was sind Hooks? schonender, als wenn wir direkt ein Plug-in abgefragt hätten. Das prädestiniert sie für AJAX-Abfrageskripte, auch wenn nicht die ganze Funktionalität verfügbar ist. Es besteht natürlich immer noch die Möglichkeit, einzelne Klassen zu inkludieren.

Abbildung 4.8 Ein neuer Kommentar wurde eingegeben.

4.5

Was sind Hooks? Es kann nötig sein, eine bestehende Extension um Funktionalität zu erweitern oder bestehende Funktionen zu ändern. Damit andere Programmierer das möglichst einfach tun können, werden sogenannte Hooks eingebaut. Hooks sind im Grunde Einsprungmarken, über die eine Extension erweitert wird. Wenn eine Extension eine andere Extension – oder auch den Core – mit einer Hook-Funktion erweitert, wird diese in der Datei ext_localconf.php

123

4 Frontend-Plug-ins registriert. Das Plug-in, das erweitert wird, führt eine Schleife über die Registrierung aus und ruft über t3lib_div::callUserFunc() die entsprechende Funktion auf. Listing 4.44 Registrierung einer Hook-Funktion $TYPO3_CONF_VARS['EXTCONF']['hooked_extkey']['nameDesHooks'] [$_EXTKEY] = 'EXT:'.$_EXTKEY. '/class.tx_extkey_hooks.php:tx_extkey_hooks->hookFunc';

“hooked_extkey” ist hierbei durch den Extension-Key der zu hookenden Extension zu ersetzen. Damit Hooks überhaupt verwendet werden können, müssen sie erst einmal vorhanden sein. Hier beginnen die Überlegungen, wo Hooks überhaupt sinnvoll sind. Da wäre zum Beispiel:

„ Hooks, um Marker in der Einzel- und Listenansicht hinzuzufügen oder zu ändern. „ Ein Hook, bevor ein Kommentar in die Datenbank geschrieben wird. Eine Möglichkeit, die sofort ins Auge springt, ist die Überprüfung der Mail-Adresse. Wir wollen sichergehen, dass auch wirklich eine Mail-Adresse eingegeben wird. Mit einer Regex-Überprüfung ist das schnell erledigt. In diesem Fall müssen wir unsere eID-Funktion hooken. Der Hook kommt direkt nach dem Befüllen des Arrays $fields. Listing 4.45 Hook einbauen 'cruser_id' => $GLOBALS['TSFE']->fe_user->user['uid'], 'pid' => $this->extconf['storageId'] ); $checkError = 0; if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['elublog'] ['commentCheck'])){ foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['elublog'] ['commentCheck'] as $userFunc) { $params = array( 'fields' => $fields, 'pObj' => &$this ); $checkError = $checkError + t3lib_div::callUserFunction($userFunc,$params,$this); } } if ($checkError > 0) {die(json_encode(array('fail' => 1)));} if ($res = $GLOBALS['TYPO3_DB']->exec_INSERTquery ('tx_elublog_blogcomments',$fields)) {

Jede Hook-Funktion muss 0 zurückliefern, wenn alles in Ordnung ist. Jeder Fehler erhöht die Variable $checkError. Ist sie, nachdem alle Hook-Funktionen abgearbeitet sind, größer als 0, wird der Kommentar nicht in die Datenbank eingetragen. Um unseren Hook zu testen, legen wir eine neue Extension namens elublogmailcheck an. Dazu muss im Kickstarter nur der Punkt General info ausgefüllt werden. Speichern Sie die Extension ab, und legen Sie eine neue Datei namens ext_localconf.php im Verzeichnis der Extension an. Diese füllen Sie mit folgendem Inhalt:

124

4.6 Flexforms Listing 4.46 ext_localconf.php hookFunc'; ?>

Der Teil zwischen den PHP-Tags kommt in eine Zeile. Wie zu sehen ist, verweist die Hook-Registrierung auf die Datei class.tx_elublogmailcheck_hooks.php, der sich aus dem Dateinamen ergebenden Klasse und der Funktion hookFunc. Legen Sie also eine Datei namens class.tx_elublogmailcheck_hooks.php an. Listing 4.47 class.tx_elublogmailcheck_hooks.php

Die Funktion hookFunc() prüft die übermittelte Mail-Adresse mit einem RegexAusdruck. Wird damit eine Mail-Adresse erkannt, liefert die Funktion 0, ansonsten 1 zurück. Somit ist es möglich, verschiedenste Arten von Prüfungen zu realisieren.

4.6

Flexforms Es wäre toll, wenn sich einige Einstellungen leichter konfigurieren ließen als über TypoScript. Und vor allem für einzelne Plug-ins, wenn sich also Einstellungen im Plug-in selbst setzen ließen. Mit Flexforms ist das möglich. Folgende Einstellungen wollen wir hier konfigurierbar machen:

„ „ „ „ „ „

Die BlogID Seite für Einzelansicht (bei Listenansicht) Seite für Listenansicht (bei Einzelansicht) Anzahl der Beiträge pro Seite Länge der Voransicht Haupttext immer in Listenansicht anzeigen

Gerade der letzte Punkt ist wichtig, werden im Moment noch die Beiträge aller eingerichteten Blogs angezeigt. Eine Möglichkeit wäre, die Tabelle tt_content um die entsprechenden Felder zu erweitern. Allerdings darf man laut den TYPO3 Coding Guidelines ei-

125

4 Frontend-Plug-ins nige Tabellen nicht mehr auf Feldbasis erweitern. Wirft man einen Blick in die Feldstruktur von tt_content, sieht man auch den Grund: Die Tabelle ist so schon sehr groß, würde jede Extension weitere Felder hinzufügen, wächst die Tabelle extrem an. Die andere Möglichkeit bieten Flexforms. Dadurch werden alle Felder als XML-Struktur in dem Feld pi_flexform der tt_content-Tabelle gespeichert. Die Definition, wie die Felder angelegt werden – im Grunde handelt es sich um eine XML-Version der TCA –, wird als Flexform bezeichnet – für flexible Forms. Die XML-Struktur, die in der Datenbank abgespeichert wird und die Daten enthält, die durch die Flexform eingegeben wurden, nennt man DS bzw. Datenstruktur. Die DS kann von dem Plug-in wieder ausgelesen werden.

4.6.1

Erzeugen der Flexform

Um eine Flexform verwenden zu können, muss sie erst einmal aktiviert werden. Das geschieht in der Datei ext_tables.php. Fügen Sie folgende Zeilen am Ende der Datei, vor dem schließenden PHP-Tag, ein. Listing 4.48 Flexform aktivieren $TCA['tt_content']['types']['list']['subtypes']['subtypes_addlist'] [$_EXTKEY.'_pi1'] = 'pi_flexform'; t3lib_extMgm::addPiFlexFormValue($_EXTKEY.'_pi1', 'FILE:EXT:'.$_EXTKEY.'/flexform_ds_pi1.xml');

Um die Flexform zu erstellen, können Sie sich am TCA orientieren, da der Aufbau der Felder im Grunde gleich ist. Listing 4.49 Input-Feld im TCA 'author' => array ( 'exclude' => 0, 'label' => 'LLL:EXT:elublog/locallang_db.xml:tx_elublog_blogentrys.author', 'config' => array ( 'type' => 'input', 'size' => '30', ) ),

Listing 4.50 Input-Feld als Flexform input <size>30

Die Grundstruktur der Flexform sieht folgendermaßen aus:

126

4.6 Flexforms Listing 4.51 Grundstruktur array <el> < … Elemete … >

Der Node <el> ist ein Platzhalter für den Namen des Elements. Im Beispiel des Elements aus Listing 4.50 wäre das . Es ist möglich, die Elemente auf verschiedenen Tabs aufzuteilen: Listing 4.52 Struktur mit Tabs <sheets> <sheet1> <sheetTitle>Sheet 1 array <el> < … Elemente … > <sheet2> <sheetTitle>Sheet 2 array <el> < … Elemente … >

Abbildung 4.9 Tabs im Flexform von tt_news

127

4 Frontend-Plug-ins Erstellen Sie für die Flexform die Datei flexform_ds_pi1.xml im Extension-Verzeichnis. Listing 4.53 flexform_ds_pi1.xml array <el> select LLL:EXT:elublog/locallang.xml:fflabel.allBlogs 0 tx_elublog_blogs <listPage> group db pages <size>1 <minitems>0 <maxsize>1 <show_thumbs>1 1 <singlePage> group db pages <size>1 <minitems>0 <maxsize>1 <show_thumbs>1 1 <entrysperpage> input <size>20 <max>40 <eval>int 1

128

4.6 Flexforms input <size>20 <max>40 <eval>trim 1 check


Die Labels werden wieder aus einer Übersetzungsdatei gefüllt. Öffnen Sie die Datei locallang.xml, und fügen Sie folgende Zeilen in die entsprechenden Übersetzungen ein. Listing 4.54 Übersetzung default index="fflabel.allBlogs">all Blogs index="fflabel.listPage">Page for ListView index="fflabel.singlePage">Page for SingleView index="fflabel.entrysperpage">Entrys per Page (0 for all) index="fflabel.teaserCrop">Crop Teasertext after ...

Listing 4.55 Übersetzung Deutsch

In Abbildung 4.10 sehen Sie das Ergebnis.

129

4 Frontend-Plug-ins

Abbildung 4.10 Flexform-Plug-in-Konfiguration

Nun müssen die Werte umgesetzt werden. In der Listenansicht sind alle Einstellungen bis auf „Seite für ‚zurück’ in Einzelansicht“ von Bedeutung. Um auf den Wert eines FlexForm-Feldes zuzugreifen, wird die Funktion pi_getFFvalue() verwendet. Listing 4.56 Zugriff auf die Flexform $var = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'element','sDEF');

Das Wort element entspricht dabei dem Namen des Feldes. In der Funktion $this-> listView() wird bestimmt, welcher Blog abgefragt wird, wie viele Beiträge pro Seite an-

gezeigt werden und auf welche Seite für die Einzelansicht verlinkt wird. Listing 4.57 Parameter bestimmen $entrysperpage = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'entrysperpage','sDEF'); $entrysperpage ($entrysperpage > 0) ? $entrysperpage : $this-> conf['listView.']['entrysPerPage']; $entrysperpage ($entrysperpage > 0) ? $entrysperpage : 10; $bid = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'bid', 'sDEF'); $bidmm = ($bid > 0) ? 'AND tx_elublog_blogentrys.bid='.$bid : ''; $bid = ($bid > 0) ? 'AND bid='.$bid : ''; $singleViewPID = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'singlePage','sDEF'); $singleViewPID = ($singleViewPID) ? $singleViewPID : $this-> conf['singleViewPID']; $this->singleViewPID = ($singleViewPID) ? $singleViewPID : 0;

Wenn in der Plug-in-Konfiguration kein Wert für die Beiträge pro Seite eingegeben wurde, wird der Wert aus der TypoScript-Konfiguration übernommen. Ist auch dieser leer, wird der Wert 10 verwendet. Genauso verhält es sich bei der Seiten-ID der Einzelansicht.

130

4.6 Flexforms Die Blog-ID (bid) der Plug-in-Konfiguration liefert die ID des darzustellenden Blogs. Diese wird in eine Erweiterung der WHERE-Klausel eingebaut, die bei der SQL-Abfrage nach den Blogbeiträgen angehängt wird. Diese Klausel sorgt dafür, dass nur Beiträge des gewählten Blogs selektiert werden. Wird der Wert 0 (alle Blogs) geliefert, wird nichts angehängt und die SQL-Abfrage demnach auch nicht auf einen Blog eingeschränkt. Damit der Wert in $entrysperpage verwendet wird, müssen wir folgende Änderung in derselben Funktion durchführen: Listing 4.58 Entrys per Page setzen // Suchen Sie folgende Zeile: $this->internal['results_at_a_time']=t3lib_div::intInRange($lConf ['results_at_a_time'],0,1000,3);

Die „3“ am Ende der Zeile ersetzen Sie mit $entrysperpage. Um die Blog-ID an die SQL-Abfrage zu übergeben, modifizieren Sie folgende Zeilen: Listing 4.59 Blog-ID setzen $res = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query('count (tx_elublog_blogentrys.uid)','tx_elublog_blogentrys','tx_elublog_blogentr ys_category_mm','tx_elublog_blogcategorys',$bidmm.' AND tx_elublog_blogentrys_category_mm.uid_foreign='.$this->piVars ['showCat']); $res = $this->pi_exec_query('tx_elublog_blogentrys',1,$bid); $res = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query('tx_elublog_blogentrys .*','tx_elublog_blogentrys','tx_elublog_blogentrys_category_mm','tx_elubl og_blogcategorys',$bidmm.' AND tx_elublog_blogentrys_category_mm .uid_foreign='.$this->piVars['showCat']); $res = $this->pi_exec_query('tx_elublog_blogentrys',0,$bid);

Fügen Sie jeweils die fett geschriebenen Teile hinzu. In der Funktion $this->getFieldContent() ändern Sie die Titelverarbeitung: Listing 4.60 Titel mit SingleViewPID verlinken case "title": return $this->pi_list_linkSingle($this->internal['currentRow'] [$fN],$this->internal['currentRow']['uid'],1,'','',$this->singleViewPID); break;

Um den „Zurück“-Link in der Einzelansicht mit der Seite für die Listenansicht zu verknüpfen, fügen Sie in die Funktion $this->singleView() folgende Zeilen am Anfang ein: Listing 4.61 Konfiguration auslesen $listViewPID = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'listPage','sDEF'); $listViewPID = ($listViewPID) ? $listViewPID : $this->conf ['listViewPID']; $listViewPID = ($listViewPID) ? $listViewPID : 0;

131

4 Frontend-Plug-ins Ist weder im Plug-in noch im TypoScript eine Seite für die Listenansicht definiert, wird 0 zurückgegeben. Das entspricht der aktuellen Seite. Auch der Marker für den Link muss angepasst werden. Listing 4.62 „Zurück“-Marker anpassen $markerArray['###BACKLINK###'] = $cObj->stdWrap($this-> pi_linkToPage($this->pi_getLL('back','Back'),$listViewPID),$this-> conf['singleView.']['backLink_stdWrap.']);

Ob in der Listenansicht der Teasertext beschnitten wird, wird ebenso wie die Anzeige des kompletten Blogbeitrags in der Funktion $this->pi_list_row() abgefragt. Folgende Zeilen gehören an den Anfang der Funktion: Listing 4.63 Die Konfiguration einlesen $tcrop = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'teaserCrop','sDEF'); $fullTextInList = $this->pi_getFFvalue($this->cObj->data['pi_flexform'], 'fullTextInList','sDEF');

Dadurch ist eine Änderung der Marker-Zuweisung nötig. Listing 4.64 Marker neu zuweisen $markerArray['###BODYTEXT###'] = (($this->getFieldContent ('bodytextinlist') == 1) || ($fullTextInList == 1)) ? $cObj->stdWrap ($this->getFieldContent('bodytext'),$this->conf['listView.'] ['bodytext_stdWrap']) : ''; … $markerArray['###TEASERTEXT###'] = $cObj->stdWrap($this->cObj>crop($this->getFieldContent('teasertext'),$tcrop),$this>conf['listView.']['teasertext_stdWrap.']);

Damit ist das Plug-in fertig für den Einsatz.

4.7

Das Plug-in pi2 Nun sollten Sie in der Lage sein, das Plug-in selbst zu erstellen. Ein paar Hinweise und Anregungen. Das Plug-in soll folgende Module bereitstellen können:

„ „ „ „

Eine Liste aller Kategorien Eine Liste aller Tags Die letzten Kommentare Die Blogroll

Verwenden Sie die Plug-in-Konfiguration, um festzulegen, welches Modul dargestellt werden soll und von welchem Blog. Werten Sie die Modulwahl mit switch/case aus, und erstellen Sie für jedes Modul eine eigene Funktion. Alle Module lassen sich mit den eben gelernten Standard-Datenbankabfragen erstellen.

132

5 5

Dokumentation und Abschluß

Jede Extension sollte ausreichend dokumentiert sein. Das betrifft nicht nur die ExtensionDokumentation, die genau erklären muss, wie die Extension verwendet wird, sondern auch den Code, der auch für andere Entwickler nachvollziehbar sein soll.

5.1

Code dokumentieren mit extdeveval Die Extension extdeveval bringt viele nützliche Funktionen für Extension-Entwickler mit. Dazu gehört auch eine Hilfe für die Code-Dokumentation nach dem phpDoc-Format. Nach der Installation finden Sie ein neues Modul bei den Admin-Werkzeugen:

Abbildung 5.1 extdeveval im Menü

Wählen Sie im Funktionsmenü des Moduls PHP script documentation help. Sie werden nun aufgefordert, eine Extension – in diesem Fall elublog – zu wählen. Dann erhalten Sie eine Liste aller Dateien. Nun können Sie alle relevanten Dateien, wie Plug-in-Klassendateien und Moduldateien, durchgehen. Die Änderungen, die ausgeführt werden, werden anhand verschiedener Farben dargestellt. Es wird ein Inhaltsverzeichnis aller Funktionen angelegt. Jede Funktion erhält einen Kommentarkopf mit einer Dummy-Beschreibung aller Parameter und des Rückgabewertes. Die korrekte Beschreibung muss nur noch hinzugefügt werden. Wenn Sie später Änderungen hinzufügen, führen Sie die Funktion einfach wieder aus, um den Funktionsindex zu aktualisieren. Durch die Zeilenangabe findet man gerade in großen Extensions leichter das Gesuchte. Die Beschreibung der Parameter und Rückgabewerte ist wichtig, um nicht erst die ganze Funktion zerpflücken zu müssen, um herauszufinden, was die Funktion erwartet und zurückgibt.

133

5 Dokumentation und Abschluß Listing 5.1 Funktionsübersicht der Datei mod2/index.php /** * [CLASS/FUNCTION INDEX of SCRIPT] * * * * 59: class tx_elublog_module2 extends t3lib_SCbase * 67: function init() * 84: function menuConfig() * 100: function main() * 125: function jumpToUrl(URL) * 168: function printContent() * 179: function moduleContent() * 196: function getNewPostButton() * 213: function getPostList() * 273: function getFunctions($table,$row) * * TOTAL FUNCTIONS: 9 * (This index is automatically created/updated by the extension "extdeveval") * */

Innerhalb der Funktion sollten Sie ebenfalls Kommentare hinterlassen, um bestimmte Abschnitte zu dokumentieren.

5.2

Extension-Dokumentation schreiben Auch wenn man gerade Männern nachsagt, nie Handbücher zu lesen, so ist eine ausführliche Dokumentation unerlässlich. Sie zeigt dem Leser nicht nur auf, wie eine Extension funktioniert, sondern auch, welche Möglichkeiten der Konfiguration zur Verfügung stehen. Nicht wenige Integratoren lehnen es ab, Extensions ohne Dokumentation zu installieren. Verwenden Sie also auf die Dokumentation dieselbe Sorgfalt wie auf die Programmierung.

Abbildung 5.2 Extensions ohne swx-Datei im /doc-Verzeichnis erzeugen einen Render-Fehler im TER.

134

5.2 Extension-Dokumentation schreiben

5.2.1

Die Vorlage

Die Dokumentation wird nicht nur in der Extension mitgeliefert, sondern auch online verfügbar gemacht. Dazu rendert das TER die Dokumentation. Nach dem Upload einer Extension steht sie nach kurzer Zeit automatisch unter http://typo3.org/documentation/document-library/extension-manuals und bei den einzelnen Extensions unter http://typo3.org/extensions/repository zur Verfügung. Aus diesem Grund muss die Dokumentation aus einer eigenen Vorlage heraus erstellt werden. Um diese Vorlage nutzen zu können, benötigen Sie OpenOffice1. Laden Sie die Vorlage von http://typo3.org/documentation/document-library/core-documentation/doc_template/current/ herunter, öffnen Sie sie in OpenOffice, und speichern Sie sie als Vorlage ab. Dokumentationen werden im /doc-Verzeichnis der Extension als OpenOffice 1.0-Dokument (.swx) abgelegt.

5.2.2

Der Aufbau der Dokumentation

Die Dokumentation hat einen vorgegebenen Aufbau, von dem nicht abgewichen werden sollte. Sie sollten nicht benötigte Abschnitte aber entfernen. Das Dokument gliedert sich in folgende Abschnitte:

„ Inhaltsangabe „ Introduction: Eine kurze Erklärung, was die Extension macht. Hier können auch Screenshots, beispielsweise von der Frontend-Ausgabe, hinterlegt werden.

„ Users Manual: Erklärt, wie die Extension vom Integrator und von den FrontendBenutzern verwendet wird.

„ Administration: Erklärt Installation, Systemvoraussetzungen und Wartung für den Administrator, einschließlich der Extension-Konfiguration über den Extension-Manager.

„ Configuration: In diesem Abschnitt wird die Konfiguration erklärt. Dazu gehört eine vollständige Auflistung aller TypoScript-Eigenschaften und der Plug-in-Konfiguration.

„ Tutorial: Eine kurze und einfache Einführung in die Extension. Am besten anhand eines Beispiels.

„ Known Problems: Probleme und Inkompatibilitäten, die dem Autor bekannt sind. „ To-Do list: Eine optionale Liste der Aufgaben und Features, die noch offen sind und in einer der späteren Versionen gelöst sein sollen.

„ Changelog: Dieser Abschnitt enthält die Änderungen an der Extension, sollte aber nicht mehr verwendet werden. Verwenden Sie für das Changelog stattdessen die Datei Change Log im Extension-Verzeichnis. Weisen Sie in der Dokumentation nur auf diese Datei hin. 1

http://www.openoffice.org

135

5 Dokumentation und Abschluß Nicht jede Extension benötigt alle diese Abschnitte. Wird ein Abschnitt nicht benötigt, löschen Sie ihn. Das sorgt wiederum für Übersicht in der Online-Dokumentation.

5.2.3

Vorlagenstile

Das TER rendert die Vorlage auf Basis der eingesetzten Stile. Dazu wurde ein Satz eigener Stile erstellt, der zu verwenden ist. Alle anderen Stile werden beim Rendern ignoriert. Tabelle 5.1 Stile in der Vorlagendatei Stil

Verwendung

Text body

Der Standardstil. Dieser kommt im normalen Text zum Tragen.

Table heading

Wird für Tabellenüberschriften verwendet

Table contents

Stil für den Tabelleninhalt

Preformatted text

Codebeispiele, TypoScript u.Ä. werden mit diesem Stil ausgezeichnet.

Heading 1

Überschriften für die Hauptabschnitte. Das TER rendert für jeden Hauptabschnitt eine neue HTML-Seite.

Heading 2

Für die Abschnitte innerhalb der Hauptabschnitte

Heading 3

Weitere Untergliederung

Heading 4

Letzte Ebene der Untergliederung

Source text

Eingefügte Codefragmente werden in diesem Stil dargestellt.

Die Verwendung von Bildern in der Dokumentation erfordert, dass die Bilder in das Dokument eingefügt werden und nicht nur darauf verwiesen wird. Dazu wählen Sie im Menü Bearbeiten > Inhalte einfügen. In dem folgenden Dialog wählen Sie Bitmap.

5.2.4

Die Dokumentation verfassen

Wenn Sie sich schließlich an das Schreiben der Dokumentation machen, behalten Sie immer im Hinterkopf, dass nicht alle Leser denselben Wissensstand wie Sie selbst haben. Gerade Anfänger sind schnell frustriert, wenn Wissen vorausgesetzt wird, das ein Anfänger noch nicht haben kann. Sie müssen keinen ausgewachsenen Roman schreiben, aber wenn Sie unsicher sind, ob Sie ein bestimmtes Detail beschreiben sollen oder nicht, sollten Sie es lieber tun. Auch Screenshots sollten nicht fehlen. Versuchen Sie, die Extension allgemeinverständlich zu beschreiben.

136

5.3 Upload ins TER

5.3

Upload ins TER Ist die Extension fertig und dokumentiert, wird es Zeit, sie in TER hochzuladen. Klicken Sie im Extension-Manager auf Ihre Extension, und wählen Sie im Extension-Menü Upload to TER. Im folgenden Dialog sollten Sie eine kurze Zusammenfassung der Änderungen formulieren. Hier wird auch die Versionsnummer gesetzt. Wenn Ihre Extension genau das tut, was Sie von ihr erwarten, scheuen Sie sich nicht, eine Hauptversion 1.0.0. zu setzen. Sollten Bugs auftauchen (was bei praktisch jeder größeren Software der Fall ist), können Sie die Versionsnummer immer noch als Bug-Fix-Version anheben.

Abbildung 5.3 Upload ins TER

Nach dem Upload wird die Extension im TER zur Verfügung gestellt und taucht im Extension-Manager auf, sofern die Extension-Liste upgedatet wurde. Bis die Dokumentation online erscheint, kann es aber ein wenig dauern. Gratulation, Ihre Extension ist fertig!

137

II Teil II – Funktionsreferenz zur klassischen Extensionprogrammierung

6 6

Datenbank

6.1 $GLOBALS['TYPO3_DB'] Insbesondere beim Zugriff auf Datenbanken ist darauf zu achten, dass ausschließlich die TYPO3-eigenen Datenbank-Handler verwendet werden. Die Klasse t3lib_DB bildet hierfür das sogenannte Datenbank-Abstraktions-Layer (DBAL). Sie ist sowohl im Backend als auch im Frontend in $GLOBALS['TYPO3_DB'] instanziiert.

6.1.1

exec_SELECTquery

$GLOBALS['TYPO3_DB']->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='');

Diese Methode führt eine Select-Abfrage aus und gibt ein Datenbankobjekt zurück. Das Objekt kann dann mittels sql_fetch_assoc oder sql_fetch_row in ein assoziatives Array umgewandelt werden (nicht mysql_fetch_assoc). Listing 6.1 Abfrage aller Frontend-User, die gerade angemeldet sind $selectRes = $GLOBALS['TYPO3_DB']->exec_SELECTquery('username', 'fe_users', 'disable = 0 AND is_online = 1');

6.1.2

exec_SELECT_queryArray

$GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryParts);

Dies ist eine Abwandlung von exec_SELECTquery, durch die gute Lesbarkeit vor allem für komplexere Abfragen sehr zu empfehlen.

141

6 Datenbank Listing 6.2 Abfrage aller Frontend-User, die gerade angemeldet sind $selectRes = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray(array( 'SELECT' => 'username', 'FROM' => 'fe_users', 'WHERE' => 'disable = 0 AND is_online = 1', 'GROUPBY' => '', 'ORDERBY' => '', 'LIMIT' => '' ));

6.1.3

exec_SELECTgetRows

$GLOBALS['TYPO3_DB']->exec_SELECTgetRows($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='', $uidIndexField='');

Führt eine Select-Abfrage aus und gibt ein assoziatives Array mit allen Ergebnissen zurück. Sehr interessant ist vor allem der letzte Parameter $uidIndexField. Falls die Abfrage ein eindeutiges Feld enthält wie z.B. uid, so werden die Keys des ausgegebenen Arrays mit diesem Feld gefüllt. Listing 6.3 Abfrage aller Frontend-User, die gerade angemeldet sind $selectRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid, username', 'fe_users', 'disable = 0 AND is_online = 1', '', '', '','uid');

6.1.4

exec_SELECT_mm_query

$GLOBALS['TYPO3_DB']->exec_SELECT_mm_query($select, $local_table, $mm_table, $foreign_table, $whereClause='', $groupBy='', $orderBy='', $limit='');

Dient zur Abfrage von n-n-Beziehungen zwischen $local_table und $foreign_table. Verknüpft sind die beiden Tabellen über $mm_table. Zu beachten ist vor allem bei gleichnamigen Feldern in $local_table und $foreign_table wie etwa uid oder pid, dass hierfür entsprechende Aliasse in $select vergeben werden. Listing 6.4 Beispiel $selectRes = $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query( 'product, name', 'tx_extkey_products', 'tx_extkey_products_category_mm', 'tx_extkey_categories'); while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($selectRes)) { $content.= $row['product'].':'.$row['name'].'
'; } /* Ergebnis in $content: Kaffee:Arabica Kaffee:Robusta */

142

6.1 $GLOBALS['TYPO3_DB']

6.1.5

exec_INSERTquery

$GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fields_values, $no_quote_fields=FALSE);

Fügt einen Datensatz in die Tabelle $table mit den im Array $field_values definierten Werten ein. Listing 6.5 Beispiel $GLOBALS['TYPO3_DB']->exec_INSERTquery('fe_users', array( 'username' => 'mickey', 'password' => 'start123', 'usergroup' => '4', 'name' => 'Mickey Maus', 'tstamp' => time() ));

6.1.6

exec_UPDATEquery

$GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields=FALSE);

Führt ein SQL Update auf $table aus. Die Bedingungen werden in $where definiert und die zu aktualisierenden Felder im Array $fields_values. Listing 6.6 Beispiel $GLOBALS['TYPO3_DB']->exec_UPDATEquery('fe_users', 'username = \'mickey\'', array('disable' => '1'));

6.1.7

exec_DELETEquery

$GLOBALS['TYPO3_DB']->exec_DELETEquery($table, $where);

Führt ein SQL Delete auf $table aus. Die Bedingungen werden in $where definiert. Listing 6.7 Beispiel $GLOBALS['TYPO3_DB']->exec_DELETEquery('fe_users', 'username = \'mickey\'');

6.1.8

sql_fetch_assoc

$GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);

Pendant zu mysql_fetch_assoc; um den Datenbank-Handler geschlossen im TYPO3-Core zu belassen, sollte auch unbedingt diese Methode genutzt werden.

143

6 Datenbank Listing 6.8 Beispiel: alphabetisch sortierte Liste aller User, die derzeit online sind $selectRes = $GLOBALS['TYPO3_DB']->exec_SELECTquery('username', 'fe_users', 'disable = 0 AND is_online = 1', '', 'username'); $usersOnline = array(); while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($selectRes)) { $usersOnline[] = $row['username']; }

6.1.9

sql_fetch_row

$GLOBALS['TYPO3_DB']->sql_fetch_row($res);

Siehe mysql_fetch_assoc, Pendant zu mysql_fetch_row.

6.1.10

searchQuery

$GLOBALS['TYPO3_DB']->searchQuery($searchWords,$fields,$table);

Erzeugt eine SQL-Abfragebedingung, um in mehreren Datenbankfeldern $fields nach den Suchbegriffen $searchWords zu suchen. Listing 6.9 Suche in den Feldern „username“ und „name“ nach „peter“ und „muster“ $whereClause = $GLOBALS['TYPO3_DB']->searchQuery(array('peter', 'muster'),array('name', 'username'), 'fe_users'); /* Ergebnis in $whereClause: (fe_users.name LIKE '%peter%' OR fe_users.username LIKE '%peter%') AND (fe_users.name LIKE '%muster%' OR fe_users.username LIKE '%muster%') */

6.1.11

listQuery

$GLOBALS['TYPO3_DB']->listQuery($field, $value, $table);

Erzeugt eine SQL-Abfragebedingung, um in kommaseparierten Listen nach einem bestimmten Wert $value zu suchen. Die kommaseaprierte Liste steht hierbei im Feld $field in der Tabelle $table. Listing 6.10 Suche im Listenfeld „Hersteller“ nach der Herstellernummer „25“ $whereClause = $GLOBALS['TYPO3_DB']->listQuery('hersteller', '25', 'tx_t3ref_produkte'); /* Ergebnis in $whereClause:

144

6.1 $GLOBALS['TYPO3_DB'] (hersteller LIKE '%,25,%' OR hersteller LIKE '25,%' OR hersteller LIKE '%,25' OR hersteller='25') */

6.1.12

splitGroupOrderLimit

$GLOBALS['TYPO3_DB']->splitGroupOrderLimit($str);

Zerlegt eine SQL-Abfrage in ein Array, optimal für exec_SELECT_queryArray. Listing 6.11 Beispiel $selectArr = $GLOBALS['TYPO3_DB']->splitGroupOrderLimit( "hersteller = '25' and lagerbestand > 0 group by menge limit 0,100"); /* Ergebnis in $selectArr: Array ( [WHERE] => hersteller = '25' and lagerbestand > 0 [GROUPBY] => menge [ORDERBY] => [LIMIT] => 0,100 ) */

6.1.13

quoteStr

$GLOBALS['TYPO3_DB']-> quoteStr($str, $table);

Ersatz der PHP-Funktion addslashes(). Listing 6.12 Beispiel mit Suchtext: Peter „PM“ Muster $quoteString = $GLOBALS['TYPO3_DB']-> quoteStr('Peter "PM" Muster', 'fe_users'); /* Ergebnis in $quoteString: Peter \"PM\" Muster */

6.1.14

fullQuoteStr

$GLOBALS['TYPO3_DB']-> fullQuoteStr($str, $table);

Wie quoteStr, jedoch mit slashes vor und hinter dem String.

145

6 Datenbank Listing 6.13 Beispiel mit Suchtext: Peter „PM“ Muster $quoteString = $GLOBALS['TYPO3_DB']-> fullQuoteStr('Peter "PM" Muster', 'fe_users'); /* Ergebnis in $quoteString: 'Peter \"PM\" Muster' */

6.1.15

fullQuoteArray

$GLOBALS['TYPO3_DB']->fullQuoteArray($arr, $table, $noQuote=FALSE);

Erweitert fullQuoteStr um die Funktionalität, ein Array abzuarbeiten. Wird Parameter $noQuote gefüllt (String oder Array), so können bestimmte Keys im Array ausgeschlossen werden, z.B. für Datenbankfelder. Listing 6.14 Beispiel mit zwei Suchtexten $quoteArray = $GLOBALS['TYPO3_DB']-> fullQuoteArray(array('Peter "PM" Muster', 'Eva "EM" Muster'), 'fe_users'); /* Ergebnis in $quoteArray: Array ( [0] => 'Peter \"PM\" Muster' [1] => 'Eva \"EM\" Muster' ) */

Listing 6.15 Beispiel mit einem Suchtext und $noQuote $quoteArray = $GLOBALS['TYPO3_DB']-> fullQuoteArray(array('select' => 'name, username', 'where' => 'Peter "PM" Muster'), 'fe_users', 'select'); /* Ergebnis in $quoteArray: Array ( [select] => name, username [where] => 'Peter \"PM\" Muster' ) */

6.1.16

escapeStrForLike

$GLOBALS['TYPO3_DB']->escapeStrForLike($str, $table);

Die Zeichen _ und % werden maskiert. Listing 6.16 Beispiel mit 100% $escapeString = $GLOBALS['TYPO3_DB']->escapeStrForLike('100%', 'tx_t3ref_bewertungen');

146

6.1 $GLOBALS['TYPO3_DB'] /* Ergebnis in $escapeString: 100\% */

6.1.17

cleanIntArray

$GLOBALS['TYPO3_DB']->cleanIntArray($arr);

Alle Werte in einem eindimensionalen Array werden in Integer-Werte umgewandelt. Listing 6.17 Beispiel $intArr = $GLOBALS['TYPO3_DB']->cleanIntArray(array(100,'69.99','5,5')); /* Ergebnis in $intArr: Array ( [0] => 100 [1] => 69 [2] => 5 ) */

6.1.18

cleanIntList

$GLOBALS['TYPO3_DB']->cleanIntList($list);

Alle Werte in einer kommaseparierten Liste werden in Integer-Werte umgewandelt. Listing 6.18 Beispiel $intList = $GLOBALS['TYPO3_DB']->cleanIntList('100,69.99,5a2'); /* Ergebnis in $intList: 100,69,5 */

6.1.19

debug_lastBuiltQuery

$GLOBALS['TYPO3_DB']->debug_lastBuiltQuery;

Enthält die letzte erzeugte SQL-Abfrage. Hierfür muss eine der beiden folgenden Optionen gesetzt sein: $GLOBALS['TYPO3_DB']->store_lastBuiltQuery = true; oder $GLOBALS['TYPO3_DB']->debugOutput = true;

147

6 Datenbank Listing 6.19 Beispiel $debugQuery = $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery; /* Ergebnis in $debugQuery: SELECT uid,username FROM fe_users WHERE disable = 0 AND is_online = 1 */

6.2 tslib_pibase Standardmäßig erweitert jedes Frontend-Plug-in die Klasse tslib_pibase. Hier finden sich einige praktische Funktionen, mit denen es möglich ist, bestimmte Abfragen in wenigen Zeilen Quellcode auszuführen.

6.2.1

pi_exec_query

$this->pi_exec_query($table, $count=0, $addWhere='', $mm_cat='', $groupBy='', $orderBy='', $query='');

Führt eine SQL-Abfrage aus, primär gedacht, um Datensätze innerhalb einer Seite abzufragen. Ist $query nicht gesetzt, so wird immer nur die aktuelle pid abgefragt mit allen aktiven Elementen, je nachdem, welche Felder in der Tabelle vorhanden sind, werden auch deleted, hidden, starttime, endtime etc. mit berücksichtigt. Listing 6.20 Beispiel: Tabelle pages: $result = $this->pi_exec_query('pages', 0); while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) $items[] = array('uid' => $row['uid'], 'title' => $row['title']); /* Ergebnis in $items: Array ( [0] => Array ( [uid] => 41 [title] => subpage 1 ) [1] => Array ( [uid] => 42 [title] => subpage 2 ) ) Ausgeführte SQL-Abfrage: SELECT pages.* FROM pages WHERE pid IN (25) AND pages.deleted=0 AND pages.t3ver_state<=0 AND pages.hidden=0 AND (pages.starttime<=1249819560) AND (pages.endtime=0 OR pages.endtime>1249819560) AND (pages.fe_group='' OR pages.fe_group IS NULL OR pages.fe_group='0' OR (pages.fe_group LIKE '%,0,%' OR pages.fe_group LIKE '0,%' OR pages.fe_group LIKE '%,0' OR pages.fe_group='0') OR (pages.fe_group LIKE '%,-1,%' OR pages.fe_group LIKE '-1,%' OR pages.fe_group LIKE '%,-1' OR pages.fe_group='-1')) LIMIT 0,20 */

148

6.2 tslib_pibase Listing 6.21 Beispiel: Tabelle pages mit count = 1: $result = $this->pi_exec_query('pages', 1); $item = $GLOBALS['TYPO3_DB']->sql_fetch_row($result); /* Ergebnis in $item: Array ( [0] => 2 ) Ausgeführte SQL-Abfrage: SELECT count(*) FROM pages WHERE pid IN (25) AND pages.deleted=0 AND pages.t3ver_state<=0 AND pages.hidden=0 AND (pages.starttime<=1249820160) AND (pages.endtime=0 OR pages.endtime>1249820160) AND (pages.fe_group='' OR pages.fe_group IS NULL OR pages.fe_group='0' OR (pages.fe_group LIKE '%,0,%' OR pages.fe_group LIKE '0,%' OR pages.fe_group LIKE '%,0' OR pages.fe_group='0') OR (pages.fe_group LIKE '%,-1,%' OR pages.fe_group LIKE '-1,%' OR pages.fe_group LIKE '%,-1' OR pages.fe_group='-1')) */

Listing 6.22 Beispiel: Tabelle fe_users mit unabhängiger pid $result = $this->pi_exec_query('fe_users', 0, '', '', '' ,'', 'FROM fe_users'); while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) $items[] = array('username' => $row['username'], 'name' => $row['name']); /* Ergebnis in $item: Array [0] [1] [2] )

( => Array ( [username] => test [name] => ) => Array ( [username] => max [name] => ) => Array ( [username] => eva [name] => )

Ausgeführte SQL-Abfrage: SELECT fe_users.* FROM fe_users LIMIT 0,20 */

6.2.2

pi_getPidList

$this->pi_getPidList($pid_list, $recursive = 0)

Erzeugt eine kommaseparierte Liste von Seiten-IDs innerhalb der ebenfalls kommaseparierten Liste $pid_list. Mit einem Wert größer 0 für $recursive kann auch um diesen Wert rekursiv gesucht werden. Listing 6.23 Beispiel: Suche alle Seiten innerhalb der Page-IDs 12 und 24 $pages = $this->pi_getPidList('24,12',2); /* Ergebnis in $pages:

149

6 Datenbank 44,25,41,42,26,24,36,12 */

6.2.3

pi_getRecord

$this->pi_getRecord($table,$uid,$checkPage=0)

Es wird ein einzelner Datensatz aus Tabelle $table abgefragt, es gibt nur die Bedingung auf eine gezielte $uid. Mit $checkPage kann zusätzlich abgefragt werden, ob dieser Datensatz auf der jeweiligen Seite verfügbar ist. Listing 6.24 Beispiel $product = $this->pi_getRecord('tx_extkey_products', 1); /* Ergebnis in $products: Array ( [uid] => 1 [pid] => 6 [tstamp] => 1255874092 [crdate] => 1255874092 [cruser_id] => 1 [product] => Kaffee [category] => 2 )

6.2.4

pi_prependFieldsWithTable

$this->pi_prependFieldsWithTable($table,$fieldList)

Fügt allen Feldern in der kommaseparierten Liste $fieldList den Tabellennamen $table vorne an. Listing 6.25 Beispiel $fields = $this->pi_prependFieldsWithTable('tx_extkey_products', 'name, category, price'); /* Ergebnis in $fields: tx_extkey_products.name,tx_extkey_products.category, tx_extkey_products.price */

150

6.3 cObj

6.3 cObj

6.3.1

DBgetDelete

$this->cObj->DBgetDelete($table, $uid, $doExec=FALSE)

„Löscht“ einen Datensatz aus Tabelle $table mit $uid als Bedingung. Sofern die Tabelle das Feld „deleted“ enthält, wird der Datensatz nicht physikalisch gelöscht, sondern es wird lediglich das Feld „deleted“ auf „1“ gesetzt. Bleibt $doExec false, so gibt die Funktion die SQL-Abfrage zurück, die ausgeführt werden würde – dies dient allerdings nur zur Information. Die SQL-Abfrage sollte immer mit $doExec = true ausgeführt werden. Im Erfolgsfall wird „1“ zurückgegeben. Listing 6.26 Beispiel $delete = $this->cObj->DBgetDelete('fe_users', '1', false); /* Ergebnis in $delete: UPDATE fe_users SET deleted='1' WHERE uid=1 */ $deleted = $this->cObj->DBgetDelete('fe_users', '1', true); /* Ergebnis in $deleted: 1 */

6.3.2

DBgetInsert

$this->cObj->DBgetInsert($table, $pid, $dataArr, $fieldList, $doExec=FALSE)

Generiert einen SQL-Insert-Befehl, der alle Systemfelder wie „tstamp“, „crdate“, „cruser_id“, „fe_cruser_id“ und „fe_crgroup_id“ automatisch berücksichtigt, sofern sie in der Tabelle vorhanden sind. Es werden ausschließlich die Werte aus dem Array $dataArr in die Tabelle $table eingefügt, die in der kommaseparierten Liste $fieldList stehen. Listing 6.27 Beispiel $dataArr = array( 'username' => 'mickey', 'password' => 'start123', 'name' => 'Mickey Maus', 'usergroup' => '2',

151

6 Datenbank 'address' => 'Entenstr. 9' ); $insert = $this->cObj->DBgetInsert('fe_users', '6', $dataArr, 'username,password,name,usergroup', false); /* Ergebnis in $insert: INSERT INTO fe_users ( username, password, name, usergroup, tstamp, crdate, cruser_id, fe_cruser_id, pid ) VALUES ( 'mickey', 'start123', 'Mickey Maus', '2', '1255884274', '1255884274', '0', '0', '6' ) */ $inserted = $this->cObj->DBgetInsert('fe_users', '6', $dataArr, 'username,password,name,usergroup', true); /* Ergebnis in $inserted: 1 */

6.3.3

DBgetUpdate

$this->cObj->DBgetUpdate($table, $uid, $dataArr, $fieldList, $doExec=FALSE)

Generiert einen SQL-Update-Befehl, der das Feld „tstamp“ automatisch berücksichtigt, sofern es vorhanden ist. Zum Ausführen muss $doExec = true gesetzt werden. Listing 6.28 Beispiel $dataArr = array( 'address' => 'Entenstr. 9' ); $update = $this->cObj->DBgetUpdate('fe_users', '11', $dataArr, 'address', false) /* Ergebnis in $update: UPDATE fe_users SET address='Entenstr. 9', tstamp='1255884909' WHERE uid=11 */ $updated = $this->cObj->DBgetUpdate('fe_users', '11', $dataArr, 'address', true) /* Ergebnis in $updated: 1 */

152

6.3 cObj

6.3.4

enableFields

$this->cObj->enableFields($table,$show_hidden=0)

Erzeugt die für SQL-Abfragen benötigten Bedingungen, um nur die aktuell „aktiven“ Datensätze zu bekommen. Es werden nur Bedingungen erzeugt für Systemfelder, die in der abgefragten Tabelle vorhanden sind. Listing 6.29 Beispiele $whereCategories = $this->cObj->enableFields('tx_extkey_categories'); /* Ergebnis in $whereCategories: AND tx_extkey_categories.deleted=0 */ $whereTTContent = $this->cObj->enableFields('tt_content', 1); /* Ergebnis in $whereTTContent; AND tt_content.deleted=0 AND tt_content.t3ver_state<=0 AND (tt_content.starttime<=1255885620) AND (tt_content.endtime=0 OR tt_content.endtime>1255885620) AND (tt_content.fe_group='' OR tt_content.fe_group IS NULL OR tt_content.fe_group='0' OR (tt_content.fe_group LIKE '%,0,%' OR tt_content.fe_group LIKE '0,%' OR tt_content.fe_group LIKE '%,0' OR tt_content.fe_group='0') OR (tt_content.fe_group LIKE '%,-1,%' OR tt_content.fe_group LIKE '-1,%' OR tt_content.fe_group LIKE '%,-1' OR tt_content.fe_group='-1')) */

153

7 7 7.1

Dateisystem

t3lib_div

7.1.1

dirname

t3lib_div::dirname($path)

Gibt einen vereinheitlichten Dateipfad zurück ohne abschließenden Slash. Listing 7.1 Beispiele $path1 $path2 $path3 $path4

= = = =

t3lib_div::dirname('/path/to/script.php'); t3lib_div::dirname('/path/to/'); t3lib_div::dirname('path/to'); t3lib_div::dirname('/script.php');

/* Ergebnisse: $path1 $path2 $path3 $path4

= = = =

'/path/to'; '/path/to '; 'path'; '';

*/

7.1.2

fixWindowsFilePath

t3lib_div::fixWindowsFilePath($path)

Bereinigt einen Windows-Dateipfad um Backslashes und doppelte Slashes.

155

7 Dateisystem Listing 7.2 Beispiel $file1 = t3lib_div::fixWindowsFilePath('d:\path\to\file.txt'); $file2 = t3lib_div::fixWindowsFilePath('file:///d:/path/to/file.txt'); /* Ergebnisse: $file1 = 'd:/path/to/file.txt'; $file2 = 'file://d:/path/to/file.txt'; */

7.1.3

formatSize

t3lib_div::formatSize($sizeInBytes,$labels='')

Formatiert die übergebene Dateigröße in Bytes, Kilo-, Mega- oder Kilobytes. Optional können die entsprechenden Bezeichnungen über $labels angepasst werden. Listing 7.3 Beispiele $filesize1 = t3lib_div::formatSize(1245000); /* Ergebnis in $filesize1: 1.2 M */ $filesize2 = t3lib_div::formatSize(1260, 'B | kB| MB| GB'); /* Ergebnis in $filesize2: 1.2 kB */ $filesize3 = t3lib_div::formatSize(1245000, 'B | kB| MB| GB'); /* Ergebnis in $filesize3: 1.2 MB */

7.1.4

get_dirs

t3lib_div::get_dirs($path)

Gibt ein Array zurück mit allen Verzeichnisnamen innerhalb von $path. Tritt beim Einlesen von $path ein Fehler auf, so gibt die Funktion den String „error“ zurück.

156

7.1 t3lib_div Listing 7.4 Beispiel $folders = t3lib_div::get_dirs('fileadmin/'); /* Ergebnis in $folders: Array ( [0] => [1] => [2] => [3] => [4] => [5] => )

flags scripts struktur styles templates user_upload

*/

7.1.5

getAllFilesAndFoldersInPath

t3lib_div::getAllFilesAndFoldersInPath(array $fileArr,$path,$extList='', $regDirs=0,$recursivityLevels=99,$excludePattern='')

Ermittelt innerhalb von $path alle Dateien und Verzeichnisse. Über das Array $fileArr können bereits zuvor ausgelesene Verzeichnisse dem Ergebnis vorangestellt, über die kommaseparierte Liste $extList bestimmte Dateierweiterungen gefiltert werden. $regDirs steuert, ob im Ergebnis-Array auch die Verzeichnisnamen separat aufgeführt werden sollen. Wie viele Ebenen rekursiv ausgelesen werden sollen, steuert $recursivityLevels. Mit Hilfe eines regulären Ausdrucks in $excludePattern können bestimmte Dateien oder Verzeichnisse ausgeschlossen werden. Listing 7.5 Beispiel $files = t3lib_div::getAllFilesAndFoldersInPath( array(), 'fileadmin/', '', 1 ); /* Auszug aus $files: Array ( [0] => fileadmin/ [65a5bd5b6c270c9628c3e90ca979996b] [1] => fileadmin/flags/ [472a2f8cda5b585bb8cb64b349e1f61c] [5cd2cbdc135d3c3a90f65b756877db77] [814da70ac0d558d15295303e3f7e249b] [6f7e898514e7baa8558a535608fbe699] [b6d02f3ee6b3ceb12644cb022bf7b444] [...] => ... )

=> fileadmin/logfile.log => => => => =>

fileadmin/flags/ar.gif fileadmin/flags/ar.png fileadmin/flags/ar_d.gif fileadmin/flags/bg.gif fileadmin/flags/bg.png

Der Array-Key ist der jeweilige md5-hash der Datei. */

157

7 Dateisystem

7.1.6

getFileAbsFileName

t3lib_div::getFileAbsFileName($filename, $onlyRelative=TRUE,$relToTYPO3_mainDir=FALSE)

Gibt den absoluten Dateipfad zur Datei in $filename an, wobei $filename auch ein Verzeichnis sein kann. $onlyRelative steuert, ob nur Dateien innerhalb des aktuellen Seitenpfades berücksichtigt werden. Standardmäßig bezieht sich der relative Dateipfad in $filename auf den aktuellen Seitenpfad, alternativ kann sich der Pfad mit $relToTYPO3_mainDir = true auf das TYPO3Core-Verzeichnis beziehen. Listing 7.6 Beispiel $file = t3lib_div::getFileAbsFileName('fileadmin/logfile.log'); /* Ergebnis in $file: /srv/www/domain.com/htdocs/fileadmin/logfile.log */

7.1.7

getFilesInDir

t3lib_div::getFilesInDir($path,$extensionList='',$prependPath=0, $order='',$excludePattern='')

Liest alle Dateien innerhalb des Verzeichnisses $path ein. $extensionList ist eine optionale kommaseparierte Liste, um nur bestimmte Dateierweiterungen einzulesen. Wird $prependPath = 1 gesetzt, so wird allen Dateinamen der Pfad vorangestellt. Mit $order = 1 wird die Dateiliste alphabetisch sortiert, $order = 'mtime' nach dem Aktualisierungsdatum. Listing 7.7 Beispiel $files = t3lib_div::getFilesInDir('fileadmin/flags/', 'png', 0, 1); /* Ergebnis in $files: Array ( [5cd2cbdc135d3c3a90f65b756877db77] [b6d02f3ee6b3ceb12644cb022bf7b444] [df58e1c2dd9a87f2b86b33b0e2cfe688] [f0aa7d05b27a5a85716c911f7e34556c] [...] => ... ) */

158

=> => => =>

ar.png bg.png bs.png ca.png

7.1 t3lib_div

7.1.8

getURL

t3lib_div::getURL($url, $includeHeader = 0, $requestHeaders = false, &$report = NULL)

Liest eine Datei oder URL aus und gibt den Dateiinhalt zurück. Optional wird der httpHeader mit ausgelesen ($includeHeader = 1). Auch der http-Header der Anfrage kann mittels $requestHeaders angepasst werden. Die Funktion nutzt cURL, setzt also ein installiertes cURL-Paket voraus. Listing 7.8 Beispiel $rss = t3lib_div::getURL('http://snippets.typo3.org/snippets.xml'); /* Ergebnis in $rss: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7.1.9

isAbsPath

t3lib_div::isAbsPath($path)

Überprüft systemunabhängig, ob der Pfad $path absolut oder relativ ist. Gibt true oder false zurück. Listing 7.9 Beispiel $isAbsolute = t3lib_div::isAbsPath('d:/path/to/file.txt'); /* Ergebnis in $isAbsolute: true */

7.1.10 isAllowedAbsPath t3lib_div::isAllowedAbsPath($path)

Überprüft, ob der Pfad $path absolut und innerhalb des Seitenpfades ist. Gibt im Erfolgsfall true zurück.

159

7 Dateisystem Listing 7.10 Beispiel $allowed = t3lib_div::isAllowedAbsPath( '/srv/www/domain.com/htdocs/fileadmin/ '); /* Ergebnis in $allowed: true */

7.1.11 mkdir t3lib_div::mkdir($path)

„Ersatz“ für die PHP-Funktion mkdir, mit dem Zweck, dass Verzeichnisrechte und Inhaber einheitlich nach der Konfiguration $GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask'] und $GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']

angelegt werden. $path muss hierbei absolut sein. Die Funktion gibt im Erfolgsfall true zurück. Listing 7.11 Beispiel $created = t3lib_div::mkdir('/srv/www/domain.com/htdocs/fileadmin/ext');

7.1.12 mkdir_deep t3lib_div::mkdir_deep($destination,$deepDir)

Erzeugt einen gesamten Verzeichnispfad auf einmal, falls notwendig. Lediglich das Verzeichnis $destination muss absolut sein und bereits existieren. Listing 7.12 Beispiel $created = t3lib_div::mkdir_deep('/srv/www/domain.com/htdocs/fileadmin/', 'ext/all/folders/to/files/');

7.1.13 removePrefixPathFromList t3lib_div::removePrefixPathFromList(array $fileArr,$prefixToRemove)

Entfernt von allen Dateipfaden im Array $fileArr den absoluten Pfad $prefixToRemove. Haben nicht alle Dateipfade den gleichen absoluten Pfad, so gibt die Funktion eine Fehlermeldung zurück.

160

7.1 t3lib_div Listing 7.13 Beispiel $files = array( '/srv/www/domain.com/htdocs/fileadmin/file1.csv', '/srv/www/domain.com/htdocs/fileadmin/ext/file2.csv' ); $relFiles = t3lib_div::removePrefixPathFromList( $files, '/srv/www/domain.com/htdocs/'); /* Ergebnis in $relFiles: Array ( [0] => fileadmin/file1.csv [1] => fileadmin/ext/file2.csv ) */

7.1.14 resolveBackPath t3lib_div::resolveBackPath($pathStr)

Löst überflüssige relative Rücksprünge im Pfad $pathStr auf. Listing 7.14 Beispiel $path = t3lib_div::resolveBackPath( 'fileadmin/first/path/../../second/path/'); /* Ergebnis in $path: fileadmin/second/path/ */

7.1.15 rmdir t3lib_div::rmdir($path,$removeNonEmpty=false)

Erweitert die PHP-Funktion rmdir um die Möglichkeit, rekursiv Verzeichnisse löschen zu können inklusive aller enthaltenen Dateien. Im Erfolgsfall gibt die Funktion true zurück. $path muss absolut sein. Listing 7.15 Beispiel $deleted = t3lib_div::rmdir('/srv/www/domain.com/htdocs/fileadmin/ext/', true);

161

7 Dateisystem

7.1.16 split_fileref t3lib_div::split_fileref($fileref)

Bricht den Pfad zu einer Datei in Pfad, Datei, Dateiname und Erweiterung auf. Listing 7.16 Beispiel $filerefParts = t3lib_div::split_fileref('fileadmin/logfile.log'); /* Ergebnis in $filerefParts: Array ( [path] => fileadmin/ [file] => logfile.log [filebody] => logfile [fileext] => log ) */

7.1.17 tempnam t3lib_div::tempnam($filePrefix)

Ersetzt die PHP-Funktion tempnam(). Alle Dateinamen werden innerhalb des Verzeichnisses typo3temp erzeugt. Listing 7.17 Beispiel $tmpfile = t3lib_div::tempnam('ext'); /* Ergebnis in $tmpfile: /path/to/typo3/htdocs/typo3temp/ext076.tmp */

7.1.18 unlink_tempfile t3lib_div::unlink_tempfile($uploadedTempFileName)

Löscht die temporäre Datei $uploadedTempFileName im Verzeichnis „typo3temp/“ und wird üblicherweise nach der Funktion t3lib_div::upload_to_tempfile() aufgerufen. Es können nur Dateien innerhalb des Verzeichnis „typo3temp/“ gelöscht werden.

7.1.19 upload_copy_move t3lib_div::upload_copy_move($source,$destination)

162

7.1 t3lib_div Verschiebt eine hochgeladene Datei in den absoluten Pfad $destination. Wird als $source keine hochgeladene Datei angegeben, wird eine Kopie der Datei erstellt. Listing 7.18 Beispiel $source = $_FILES['uploadedfile']['tmp_name']; $destination = t3lib_div::getFileAbsFileName('uploads'). '/'.$this->extKey.'/'. str_replace(" ","_",$_FILES['uploadedfile']['name']); $uploaded = t3lib_div::upload_copy_move($source,$destination); /* Ergebnis in $uploaded: true */

7.1.20 upload_to_tempfile t3lib_div::upload_to_tempfile($uploadedFileName)

Verschiebt eine hochgeladene Datei ins Verzeichnis typo3temp mit temporärem Dateinamen. Der Rückgabewert enthält im Erfolgsfall den Dateinamen. Listing 7.19 Beispiel $source = $_FILES['uploadedfile']['tmp_name']; $filename = t3lib_div::upload_to_tempfile($source); /* Ergebnis in $filename: /srv/www/domain.tld/htdocs/typo3temp/upl1D5.tmp */

7.1.21 verifyFilenameAgainstDenyPattern t3lib_div::verifyFilenameAgainstDenyPattern($filename)

Überprüft den übergebenen Dateinamen gegen die Konfiguration in $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern']

und gibt false zurück, sobald der Dateiname übereinstimmt.

163

7 Dateisystem

7.1.22 writeFile t3lib_div::writeFile($file,$content)

Schreibt den String $content in die Datei $file. Existiert $file bereits, so wird die Datei überschrieben. Listing 7.20 Beispiel $written = t3lib_div::writeFile('fileadmin/data.txt','hello world'); /* Ergebnis in $written: true */

7.1.23 writeFileToTypo3tempDir t3lib_div::writeFileToTypo3tempDir($filepath,$content)

Schreibt $content in eine Datei innerhalb des Verzeichnisses typo3temp. $filepath muss den absoluten Pfad enthalten, es werden auch Verzeichnisse unterhalb von typo3temp unterstützt. Im Erfolgsfall gibt die Funktion false zurück. Listing 7.21 Beispiel $written = t3lib_div::writeFileToTypo3tempDir( PATH_site.'typo3temp/data.txt','hello world' ); /* Ergebnis in $written: false */

164

8 8

8.1

Strings, Arrays und Umgebungsvariablen

Allgemeine Funktionen

8.1.1

_GET

t3lib_div::_GET($var=NULL)

Gibt alle Werte des globalen GET-Arrays zurück. Da die Werte normalisiert und alle Escape-Zeichen entfernt werden, sollte jeder Zugriff auf GET-Variablen über diese Methode erfolgen. Wird $var gesetzt, erfolgt als Rückgabe nur der Wert von $var. Listing 8.1 Aufruf einer URL mit „&showArticle=54“ $articleID = t3lib_div::_GET('showArticel'); $getVars = t3lib_div::_GET(); /* Ergebnis in $articleID: 54 Ergebnis in $getVars: Array ( [id] => 40 [showArticle] => 54 ) */

8.1.2

_GETset

t3lib_div::_GETset($inputGet,$key='')

Schreibt Eingabe Werte in $_GET

165

8 Strings, Arrays und Umgebungsvariablen Wird $key nicht gesetzt, so wird das gesamte Array $_GET überschrieben! Mit definiertem $key wird nur dieser Bereich neu geschrieben. Listing 8.2 Beispiel t3lib_div::_GETset(array("name" => "mickey maus"));

8.1.3

_GP

t3lib_div::_GP($var,$strip=0)

Gibt den Wert von $var zurück, wobei $var sowohl eine GET- als auch eine POSTVariable sein kann. Sollten beide Variablen vorhanden sein, so wird die POST-Variable zurückgegeben. Mit $strip=1 werden nur Arrays normalisiert. Listing 8.3 Beispiel $name = t3lib_div::_GP('name'); /* Ergebnis in $name: mickey maus */

8.1.4

_POST

t3lib_div::_POST($var=NULL)

Gleiche Funktionalität wie _GET für _POST-Variablen Listing 8.4 Beispiel $articleID = t3lib_div::_POST('showArticel'); $getVars = t3lib_div::_POST(); /* Ergebnis in $articleID: 54 Ergebnis in $getVars: Array ( [id] => 40 [showArticle] => 54 ) */

8.1.5

callUserFunction

t3lib_div::callUserFunction( $funcName,&$params,&$ref,$checkPrefix='user_',$errorMode=0 )

166

8.1 Allgemeine Funktionen Führt eine benutzerdefinierte Funktion aus. Standardmäßig sollten diese Funktionen mit dem Präfix „user_“ beginnen. Die Parameter werden als Referenz übergeben. Listing 8.5 Beispiel $userVar = array (); t3lib_div::callUserFunction('user_Func',$userVar,$this); function user_Func($refVar) { $refVar = array(1 => 'hello world'); } /* Ergebnis in $userVar: Array ( [1] => hello world ) */

8.1.6

clientInfo

t3lib_div::clientInfo($useragent='')

Basisinformationen über den Browser und das Betriebssystem des Clients. Listing 8.6 Beispiel $client = t3lib_div::clientInfo(); /* Ergebnis in $client: Array ( [BROWSER] => net [VERSION] => 5 [SYSTEM] => win [FORMSTYLE] => 1 ) */

8.1.7

compat_version

t3lib_div::compat_version($verNumberStr)

Überprüft die Kompatibilität zur übergebenen TYPO3-Version Listing 8.7 Beispiel $check = t3lib_div::compat_version("4.2.0"); /* Ergebnis in $check: 1 */

167

8 Strings, Arrays und Umgebungsvariablen

8.1.8

compileSelectedGetVarsFromArray

t3lib_div::compileSelectedGetVarsFromArray( $varList,array $getArray,$GPvarAlt=1 )

Bildet einen Auszug aus einem eindimensionalen Array, gesteuert über eine kommaseparierte Liste von keys. Wird kein Array definiert, so werden die GET- und POST-Variablen verwendet (Reihenfolge siehe _GP). Listing 8.8 Beispiel $vars = t3lib_div::compileSelectedGetVarsFromArray('name,email',array()); /* Ergebnis ind $vars: Array ( [name] => mickey maus [email] => [email protected] ) */

8.1.9

getHostname

t3lib_div::getHostname()

Gibt den Fully Qualified Domain Name zurück. Listing 8.9 Beispiel t3lib_div::getHostname()

8.1.10 getIndpEnv t3lib_div::getIndpEnv($getEnvName)

Pendant zu $_SERVER, allerdings systemunabhängig. Listing 8.10 Gültige Werte für $getEnvName: SCRIPT_NAME, SCRIPT_FILENAME, REQUEST_URI, PATH_INFO, REMOTE_ADDR, REMOTE_HOST, HTTP_REFERER, HTTP_HOST, HTTP_USER_AGENT, HTTP_ACCEPT_LANGUAGE, QUERY_STRING, TYPO3_DOCUMENT_ROOT, TYPO3_HOST_ONLY, TYPO3_HOST_ONLY, TYPO3_REQUEST_HOST, TYPO3_REQUEST_URL, TYPO3_REQUEST_SCRIPT, TYPO3_REQUEST_DIR, TYPO3_SITE_URL, _ARRAY

8.1.11 getThisUrl t3lib_div::getThisUrl()

Gibt den Host und Pfad zur aktuellen Seite zurück.

168

8.1 Allgemeine Funktionen Listing 8.11 Beispiel $url = t3lib_div::getThisUrl(); /* Ergebnis in $url: domain.com/path/to/script/ */

8.1.12 linkThisScript t3lib_div::linkThisScript(array $getParams = array())

Erzeugt die vollständige URL zum aktuellen Skript, über das Array $getParams können die mit zu übergebenden GET-Parameter gesteuert werden. Ein leeres Array entfernt alle GET-Parameter. Listing 8.12 Beispiel $url = t3lib_div::linkThisScript(array()); /* Ergebnis in $url: /index.php?id=40 */

8.1.13 linkThisUrl t3lib_div::linkThisUrl($url,array $getParams=array())

Die in $url übergebene vollständige URL wird geparst und mit den in $getParams übergebenen Parametern ergänzt, bestehende werden überschrieben. Listing 8.13 Beispiel $url = t3lib_div::linkThisUrl('http://www.domain.com/produkte.html?productid=lee r',array('productid' => '354', 'category' => 'coffee')) /* Ergebnis in $url: http://www.domain.com/produkte.html?productid=354&category=coffee */

8.1.14 locationHeaderUrl t3lib_div::locationHeaderUrl($path)

Erzeugt aus relativen und absoluten Pfaden vollständige URLs.

169

8 Strings, Arrays und Umgebungsvariablen Listing 8.14 Beispiel $url = t3lib_div::locationHeaderUrl('/absolute/path/to/script.html'); /* Ergebnis in $url: http://www.domain.com/absolute/path/to/script.html */ $url = t3lib_div::locationHeaderUrl('relativepath/script.html'); /* Ergebnis in $url: http://www.domain.com/current/path/relativepath/script.html */

8.1.15 makeInstance t3lib_div::makeInstance($className)

Erzeugt eine neue Instanz der Klasse $className – sollte unbedingt statt der PHPMethode „new“ verwendet werden. Listing 8.15 Beispiel $obj =

t3lib_div::makeInstance('tx_extkey_functions');

8.1.16 makeInstanceService t3lib_div::makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=array())

Erzeugt eine neue Instanz eines Service und gibt die Service-Class als Objekt zurück Listing 8.16 Beispiel if (is_object($service = t3lib_div::makeInstanceService('myService'))) { $content = $serviceObj->main($value); }

8.1.17 rmFromList t3lib_div::rmFromList($element,$list)

Löscht den Inhalt von $element aus der kommaseparierten Liste $list.

170

8.2 String-Funktionen Listing 8.17 Beispiel $newList = t3lib_div::rmFromList('24', '12,26,24,1,4'); /* Ergebnis in $newList: 12,26,1,4 */

8.1.18 sysLog t3lib_div::sysLog($msg, $extKey, $severity=0) Schreibt den Inhalt von $msg in den syslog des Servers. Listing 8.18 Gültige Werte für $severity: 0 1 2 3 4

8.2

= = = = =

info, notice, warning, error, fatal error

String-Funktionen

8.2.1

calcParenthesis

t3lib_div::calcParenthesis($string)

Berechnet den übergebenen String unter der Berücksichtigung von Klammern. Listing 8.19 Beispiel $value = t3lib_div::calcParenthesis('(4+20)/4'); /* Ergebnis on $value: 6 */

8.2.2

cmpFQDN

t3lib_div::cmpFQDN($baseIP, $list)

171

8 Strings, Arrays und Umgebungsvariablen Vergleicht den Fully Qualified Domain Name der $baseIP mit einer in $list übergebenen kommaseparierten Liste. Diese Liste darf Wildcards enthalten. Bei Übereinstimmung gibt die Funktion true zurück, ansonsten false. Listing 8.20 Beispiele für $list: subdomain.*.com *.domain.com Falsch wäre: sub*.domain.com

8.2.3

cmpIP

t3lib_div::cmpIP($baseIP, $list)

Vergleicht die IP mit einer in $list übergebenen kommaseparierten Liste. Diese Liste darf Wildcards enthalten. Bei Übereinstimmung gibt die Funktion true zurück, ansonsten false. Es werden sowohl IPv4 als auch IPv6 unterstützt. Listing 8.21 Beispiel für $list 192.168.10.*,192.168.11.*

8.2.4

convUmlauts

t3lib_div::convUmlauts($str)

Wandelt die deutschen Umlaute um. Listing 8.22 Beispiel $string = t3lib_div::convUmlauts('Ä Ö Ü ä ö ü ß'); /* Ergebnis in $string: Ae Oe Ue ae oe ue ss */

8.2.5

csvValues

t3lib_div::csvValues(array $row,$delim=',',$quote='"')

Erzeugt aus einem eindimensionalen Array eine Zeile csv.

172

8.2 String-Funktionen Listing 8.23 Beispiel $csv = t3lib_div::csvValues(array('13','coffee','beans')); /* Ergebnis in $csv: "13","coffee","beans" */

8.2.6

deHSCentities

t3lib_div::deHSCentities($str)

Macht die Bearbeitung von HTML-Entities mit htmlspecialchars rückgängig. Listing 8.24 Beispiel $str = t3lib_div::deHSCentities("""); /* Ergebnis in $str: " */

8.2.7

expandList

t3lib_div::expandList($list)

Eine kommaseparierte Liste von Integer-Werten mit Bereichen „von-bis“ wird als erweiterte kommaseaprierte Liste zurückgegeben mit allen Einzelwerten innerhalb dieser Bereiche. Listing 8.25 Beispiel $list = t3lib_div::expandList("1,5,6-20,98"); /* Ergebnis in $list: 1,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,98 */

8.2.8

fixed_lgd

t3lib_div::fixed_lgd_cs($string,$origChars)

Kürzt den String $string am Ende mit $preStr ab, sofern dieser mehr Zeichen enthält als $origChars.

173

8 Strings, Arrays und Umgebungsvariablen Listing 8.26 Beispiel $str = t3lib_div::fixed_lgd_cs("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt", 30); /* Ergebnis in $str: Lorem ipsum dolor sit amet,... */

8.2.9

fixed_lgd_pre

t3lib_div::fixed_lgd_pre($string,$chars)

Kürzt den String $string am Ende mit „...“ ab, sofern dieser mehr Zeichen enthält als $origChars. Listing 8.27 Beispiel $str = t3lib_div::fixed_lgd_pre("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt.", 36); /* Ergebnis in $str: ...sed do eiusmod tempor incididunt. */

8.2.10 formatForTextarea t3lib_div::formatForTextarea($content)

Optimierte Variante von htmlspecialchars(), sollte für jede Textarea verwendet werden. Listing 8.28 Beispiel $content = t3lib_div::formatForTextarea($content);

8.2.11 generateRandomBytes t3lib_div::generateRandomBytes($count)

Erzeugt einen Zufalls-String mit der Anzahl von $count-Zeichen. Als Bereich werden die gesamten 8 Bit genutzt.

174

8.2 String-Funktionen Listing 8.29 Beispiel $random = t3lib_div::generateRandomBytes(20); /* Ergebnis in $random: uðU†+é<耿62Ý|Q ˆ¶X */

8.2.12 get_tag_attributes t3lib_div::get_tag_attributes($tag)

Gibt ein Array zurück, das alle Attribute mit Werten eines HTML-Tags enthält. Listing 8.30 Beispiel $attr = t3lib_div::get_tag_attributes(''); /* Ergebnis in $attr: Array ( [action] => /anmeldung.html [method] => get [target] => main )

8.2.13 htmlspecialchars_decode t3lib_div::htmlspecialchars_decode($value)

Umkehrfunktion zur PHP-Funktion htmlspecialchars(). Listing 8.31 Beispiel $value = t3lib_div::htmlspecialchars_decode('&'); /* Ergebnis in $value: & */

8.2.14 implodeArrayForUrl t3lib_div::implodeArrayForUrl($name,array $theArray, $str='',$skipBlank=0,$rawurlencodeParamName=0)

Erzeugt aus dem multidimensionalen Array $theArray einen String, der die Wertepaare des Arrays als GET-Parameter enthält.

175

8 Strings, Arrays und Umgebungsvariablen $name ist hierbei ein Präfix für alle Keys des Arrays. Wird $skipBlank gesetzt, so werden nur GET-Parameter geschrieben, die auch einen Wert übergeben. $rawurlencodeParamName steuert die PHP-Funktion rawurlencode() für die Keys. Listing 8.32 Beispiel $url = t3lib_div::implodeArrayForUrl('product', array( 'id' => '52345', 'category' => '', 'group' => 'coffee,beans' ), '', 1); /* Ergebnis in $url: &product[id]=52345&product[group]=coffee%2Cbeans */

8.2.15 implodeAttributes t3lib_div::implodeAttributes(array $arr, $xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE)

Erzeugt aus einem Array Attribute für bspw. ein html-Tag. $xhtmlSafe = true wird hier empfohlen, da diese Option gleichzeitig eventuell doppelt vorkommende Attribute ignoriert (hier wird jeweils das erste verwendet) und die Werte durch htmlspecialchars() bereinigt. Standardmäßig werden leere Attribute nicht berücksichtigt; falls allerdings $dontOmitBlankAttributes gesetzt ist, werden auch leere Attribute zurückgegeben. Listing 8.33 Beispiel $attr = t3lib_div::implodeAttributes(array( 'action' => '/anmeldung.html', 'Method' => 'get', 'target' => 'anmeldeformular' ), true); /* Ergebnis in $attr: action="/anmeldung.html" method="get" target="anmeldeformular" */

8.2.16 inList t3lib_div::inList($list, $item)

Sucht nach dem Element $item in der kommaseparierten Liste $list. Ist das Element vorhanden, gibt die Funktion true zurück, ansonsten false.

176

8.2 String-Funktionen Listing 8.34 Beispiel $status = (t3lib_div::inList('1,5,8,13,29,rehw,r7345j', 'reh')) ? 'gefunden' : 'nicht gefunden'; /* Ergebnis in $status: nicht gefunden */

8.2.17 int_from_ver t3lib_div::int_from_ver($verNumberStr)

Erzeugt einen Integer-Wert aus einer Versionsnummer im Format „x.x.x“, wobei jeder Teil der Versionsnummer bis 999 zählt. Listing 8.35 Beispiel $ver = t3lib_div::int_from_ver('5.0.13'); /* Ergebnis in $ver: 5000013 */

8.2.18 intInRange t3lib_div::intInRange($theInt,$min,$max=2000000000,$zeroValue=0)

Überprüft, ob $theInt innerhalb der Werte $min und $max liegt. Ist der Wert kleiner oder größer, so wird entweder $min oder $max ausgegeben. Falls der Wert kein Integer-Wert ist, wird er durch $zeroValue ersetzt. Listing 8.36 Beispiele $res[0] = t3lib_div::intInRange(5, 10); $res[1] = t3lib_div::intInRange(5, 1, 10); $res[2] = t3lib_div::intInRange('v', 1, 10); $res[3] = t3lib_div::intInRange(100, 1, 10); /* Ergebnis in $res: Array ( [0] => [1] => [2] => [3] => )

10 5 1 10

177

8 Strings, Arrays und Umgebungsvariablen

8.2.19 intval_positive t3lib_div::intval_positive($theInt)

Falls $theInt negativ ist, wird 0 zurückgegeben, positive Werte bleiben unverändert. Listing 8.37 Beispiele $res[0] = t3lib_div::intval_positive(-5); $res[1] = t3lib_div::intval_positive(10); $res[2] = t3lib_div::intval_positive('v'); /* Ergebnis in $res: Array ( [0] => 0 [1] => 10 [2] => 0 )

8.2.20 isFirstPartOfStr t3lib_div::isFirstPartOfStr($str, $partStr)

Überprüft, ob $partStr mit dem Anfang von $str übereinstimmt, und gibt true oder false zurück. Listing 8.38 Beispiel $str = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt"; $partStr =

"Lorem ipsum dolor sit amet";

$isFirstPart = t3lib_div::isFirstPartOfStr($str, $partStr); /* Ergebnis in $isFirstPart: true */

8.2.21 md5int t3lib_div::md5int($str)

Wandelt die ersten sieben Stellen des md5-Hash von $str in den Integer-Wert um, quasi der Integer-Hash von $str.

178

8.2 String-Funktionen Listing 8.39 Beispiel $intHash = t3lib_div::md5int('coffee'); /* Ergebnis in $intHash: 38711389 */

8.2.22 milliseconds t3lib_div::milliseconds()

microtime() in Millisekunden Listing 8.40 Beispiel $ms = t3lib_div::milliseconds(); /* Ergebnis in $ms: 1256537906427 */

8.2.23 modifyHTMLColor t3lib_div::modifyHTMLColor($color,$R,$G,$B)

Berechnet eine neue Farbe, indem den jeweiligen Farben die Offsets $R, $G und $B dazugerechnet werden. Der gültige Wertebereich liegt zwischen 0 und 255. Listing 8.41 Beispiel $newColor = t3lib_div::modifyHTMLColor('#000000', '155', '0', '255'); /* Ergebnis in $newColor: #9b00ff */

8.2.24 modifyHTMLColorAll t3lib_div::modifyHTMLColorAll($color,$all)

Der Farbe $color wird ein gleichmäßiges Offset dazugerechnet (Aufhellung). Die Werte des Offsets $all können zwischen 0 und 255 liegen.

179

8 Strings, Arrays und Umgebungsvariablen Listing 8.42 Beispiel $newColor = t3lib_div::modifyHTMLColorAll('#000000', '155'); /* Ergebnis in $newColor: #9b9b9b */

8.2.25 normalizeIPv6 t3lib_div::normalizeIPv6($address)

Normalisiert eine komprimierte Darstellung einer IPv6-Adresse $address. Listing 8.43 Beispiel $ipv6 = t3lib_div::normalizeIPv6('3ffe:400:280::1'); /* Ergebnis in $ipv6: 3ffe:0400:0280:0000:0000:0000:0000:0001 *&

8.2.26 removeXSS t3lib_div::removeXSS($string)

Entfernt eventuellen Cross-Site-Scripting-Code im Input-Wert $string. Listing 8.44 Beispiel $input = t3lib_div::removeXSS(" alert(\"XSS\")'); ?> "); /* Ergebnis in $input: alert("XSS")<x>ript>'); ?> */

8.2.27 revExplode t3lib_div::revExplode($delim, $string, $count=0)

Pendant zur PHP-Funktion explode(), allerdings wird der String rückwärts bearbeitet. Zusätzlich kann ein Limit $count angegeben werden, ist die Anzahl dieser Ergebnisse erreicht, bricht die Funktion ab.

180

8.2 String-Funktionen Listing 8.45 Beispiel $values = t3lib_div::revExplode(',', '1,5,8,10,15,24', 4); /* Ergebnis in $values: Array ( [0] => [1] => [2] => [3] => )

1,5,8 10 15 24

*/

8.2.28 rm_endcomma t3lib_div::rm_endcomma($string)

Falls am Ende des Strings $string ein Komma steht, wird es entfernt. Listing 8.46 Beispiel $list = t3lib_div::rm_endcomma('1,5,8,10,15,'); /* Ergebnis in $list: 1,5,8,10,15 */

8.2.29 shortMD5 t3lib_div::shortMD5($input, $len=10)

Gibt $len Stellen des md5-Hash von $input zurück. Listing 8.47 Beispiel $hash = t3lib_div::shortMD5('coffee'); /* Ergebnis: 24eb05d183 */

8.2.30 split_tag_attributes t3lib_div::split_tag_attributes($tag)

Erzeugt ein Array mit allen Attributen des Tags $tag.

181

8 Strings, Arrays und Umgebungsvariablen Listing 8.48 Beispiel $attr = t3lib_div::split_tag_attributes(' '); /* Ergebnis in $attr: Array ( [0] => [1] => [2] => [3] => [4] => [5] => [6] => [7] => [8] => )

class = linkNewWindow href = url.html onclick = openWindow(this.href); return false;

*/

8.2.31 splitCalc t3lib_div::splitCalc($string,$operators)

Teilt den String $string mit allen Zeichen in $operators. Rückgabewert ist ein Array mit allen String-Teilen in der ursprünglichen Reihenfolge. Listing 8.49 Beispiel $split = t3lib_div::splitCalc('123 + 456 / 789 - 4', '/+-*'); /* Ergebnis in $split: Array ( [0] => [0] [1] ) [1] => [0] [1] ) [2] => [0] [1] ) [3] => [0] [1] ) ) */

182

Array ( => + => 123 Array ( => + => 456 Array ( => / => 789 Array ( => => 4

8.2 String-Funktionen

8.2.32 strtolower t3lib_div::strtolower($str)

Abwandlung der PHP-Funktion strtolower(); es werden nur Zeichen aus dem latinZeichensatz berücksichtigt (a–z, A–Z), Sonderzeichen und Umlaute bleiben unberührt. Listing 8.50 Beispiel $lowerStr = t3lib_div::strtolower('AÁÀÄ'); /* Ergebnis in $lowerStr: aÁÀÄ */

8.2.33 strtoupper t3lib_div::strtoupper($str)

Abwandlung der PHP-Funktion strtoupper(); es werden nur Zeichen aus dem latinZeichensatz berücksichtigt (a–z, A–Z), Sonderzeichen und Umlaute bleiben unberührt. Listing 8.51 Beispiel $upperStr = t3lib_div::strtoupper('aáàä'); /* Ergebnis in $upperStr: Aáàä */

8.2.34 substUrlsInPlainText t3lib_div::substUrlsInPlainText( $message,$urlmode='76',$index_script_url='' )

Um lange URLs in Nur-Text-E-Mails zu vermeiden, wandelt diese Funktion lange URLs in Redirects mit Hash um. Die Original-URL wird in der Datenbank abgespeichert. Listing 8.52 Beispiel $redirect = t3lib_div::substUrlsInPlainText( "http://foreign.tld/shop/produkte/kaffee/details/bohnen/espresso/ arabica/1kg/index.html" );

183

8 Strings, Arrays und Umgebungsvariablen /* Ergebnis in $redirect: http://site.tld/pg01/index.php?RDCT= a2d1dff3fd0c51312d70 */

8.2.35 testInt t3lib_div::testInt($var)

Überprüft, ob die Variable $var ein Integer-Wert ist. Listing 8.53 Beispiel $res[0] = t3lib_div::testInt(4); $res[1] = t3lib_div::testInt('4.5'); $res[2] = t3lib_div::testInt('-3'); $res[3] = t3lib_div::testInt('d4'); /* Ergebnis in $res: Array ( [0] => 1 [1] => [2] => 1 [3] => ) */

8.2.36 trimExplode t3lib_div::trimExplode($delim, $string, $onlyNonEmptyValues=0)

Kombiniert die PHP-Funktionen explode() und trim(). Ist $onlyNonEmptyValues gesetzt, werden leere Werte ignoriert. Listing 8.54 Beispiel $values = t3lib_div::trimExplode('|', 'rt | /* Ergebnis in $values Array ( [0] => rt [1] => 2 [2] => f ) */

184

|2 |f | ', 1);

8.2 String-Funktionen

8.2.37 uniqueList t3lib_div::uniqueList($in_list)

Entfernt alle Duplikate und leere Werte in $in_list. $in_list kann entweder ein Array oder eine kommaseparierte Liste sein. Listing 8.55 Beispiel $list = t3lib_div::uniqueList('1,5,8,,3,4,8,5,9'); /* Ergebnis in $list: 1,5,8,3,4,9 */

8.2.38 validEmail t3lib_div::validEmail($email)

Überprüft $email auf eine gültige E-Mail-Adresse. Es wird nur die Syntax geprüft. Listing 8.56 Beispiel $valid = t3lib_div::validEmail('[email protected]'); /* Ergebnis in $valid: true */

8.2.39 validIP t3lib_div::validIP($ip)

Überprüft $ip auf eine gültige Syntax im IPv4- oder IPv6-Format. Listing 8.57 Beispiel $valid = t3lib_div::validIP('10.0.45.99'); /* Ergebnis in $valid: true */

185

8 Strings, Arrays und Umgebungsvariablen

8.2.40 validIPv4 t3lib_div::validIPv4($ip)

Überprüft $ip auf eine gültige Syntax im IPv4-Format. Listing 8.58 Beispiel $valid = t3lib_div::validIPv4('10.0.45.99'); /* Ergebnis in $valid: true */

8.2.41 validIPv6 t3lib_div::validIPv6($ip)

Überprüft $ip auf eine gültige Syntax im IPv6-Format, die komprimierte Schreibweise ist nicht gültig. Listing 8.59 Beispiel $valid = t3lib_div::validIPv6('3ffe:0400:0280:0000:0000:0000:0000:0001'); /* Ergebnis in $valid: true */

8.3

Array-Funktionen

8.3.1

addSlashesOnArray

t3lib_div::addSlashesOnArray(array &$theArray)

Maskiert alle Werte im multidimensionalen Array $theArray, das als Referenz bearbeitet wird. Listing 8.60 Beispiel $values = array('"Lorem"', '"ipsum"'); t3lib_div::addSlashesOnArray($values);

186

8.3 Array-Funktionen /* Ergebnis in $values: Array ( [0] => \"Lorem\" [1] => \"ipsum\" ) */

8.3.2

array_merge

t3lib_div::array_merge(array $arr1,array $arr2)

Verknüpft die beiden Arrays $arr1 und $arr2, wobei alle Keys erhalten bleiben. Kommt der gleiche Key in beiden Arrays vor, so überschreibt $arr2 den Wert aus $arr1. Listing 8.61 Beispiel $arr1 = "0" "2" "4" ); $arr2 = "1" "3" "5" "4" );

array( => "value0", => "value2", => "value4" array( => "value1", => "value3", => "value5", => "value41"

$merged = t3lib_div::array_merge($arr1, $arr2); /* Ergebnis in $merged: Array ( [1] => [3] => [5] => [4] => [0] => [2] => )

value1 value3 value5 value41 value0 value2

*/

8.3.3

array_merge_recursive_overrule

t3lib_div::array_merge_recursive_overrule( array $arr1,array $arr2,$notAddKeys=0,$includeEmtpyValues=true )

Erweiterung der Funktion t3lib_div::array_merge(). Es bleibt die Original-Reihenfolge erhalten, trotzdem überschreibt $arr2 bei identischen Keys die Werte von $arr1. Optional können neue Keys vergeben ($notAddKeys=1) und leere Werte übernommen werden ($includeEmptyValues=false).

187

8 Strings, Arrays und Umgebungsvariablen Listing 8.62 Beispiel $arr1 = "0" "2" "4" ); $arr2 = "1" "3" "5" "4" );

array( => "value0", => "value2", => "value4" array( => "value1", => "value3", => "value5", => "value41"

$merged = t3lib_div:: array_merge_recursive_overrule($arr1, $arr2); /* Ergebnis in $merged: Array ( [0] => [2] => [4] => [1] => [3] => [5] => )

value0 value2 value41 value1 value3 value5

*/

8.3.4

array2json

t3lib_div::array2json(array $jsonArray)

Konvertiert ein multidimensionales Array in einen JSON-String. Listing 8.63 Beispiel $arr = array( "beans" => array( "uid" => 56, "name" => "arabica", ), "milk" => false ); $jsonString = t3lib_div::array2json($arr); /* Ergebnis in $jsonString {"beans":{"uid":56,"name":"arabica"},"milk":false} */

8.3.5

array2xml

t3lib_div::array2xml( array $array, $NSprefix='', $level=0,

188

8.3 Array-Funktionen $docTag='phparray', $spaceInd=0, array $options=array(), array $stackData=array() )

Erzeugt aus dem multidimensionalen Array $array einen xml-String. Listing 8.64 Beispiel $arr = array( "beans" => array( "uid" => 56, "name" => "arabica", ), "milk" => false ); $xml = t3lib_div::array2xml($arr); /* Ergebnis in $xml: 56 arabica <milk type="boolean"> */

8.3.6

arrayToLogString

t3lib_div::arrayToLogString( array $arr, $valueList=array(), $valueLength=20 )

Konvertiert ein eindimensionales Array in einen String, der beispielsweise zum Erstellen von Logfiles gut geeignet ist. Die Wertepaare werden nacheinander geschrieben, getrennt durch „ ; „. Standardmäßig werden die Werte nach 20 Zeichen abgeschnitten. Listing 8.65 Beispiel $log = array ( "importfile" => "test412.csv", "timestamp" => "1234567890", "records" => "412", "success" => "412" ); $logContent = t3lib_div::arrayToLogString($log); /* Ergebnis in $logContent: importfile: test412.csv; timestamp: 1234567890; records: 412; success: 412; */

189

8 Strings, Arrays und Umgebungsvariablen

8.3.7

explodeUrl2Array

t3lib_div::explodeUrl2Array($string,$multidim=FALSE)

List eine url in $string ein und erzeugt ein Array mit allen GET-Parametern. Optional kann mit $multidim=true auch ein mehrdimensionales Array erzeugt werden, sofern die GETParameter Variablennamen in eckigen Klammern enthalten. Listing 8.66 Beispiel $url = '&extkey1[var]=14&extkey1[name]=coffee&extkey2[group]=25'; $values = t3lib_div::explodeUrl2Array($url, true); /* Ergebnis in $values: Array ( [extkey1] => Array ( [var] => 14 [name] => coffee ) [extkey2] => Array ( [group] => 25 ) ) */

8.3.8

inArray

t3lib_div::inArray(array $in_array, $item)

Optimierte Version der PHP-Funktion in_array(), sie durchsucht das Array $in_array eindimensional nach $item. Listing 8.67 Beispiel $found = t3lib_div::inArray(array('55', '4', '62', '48'), '4'); /* Ergebnis in $found: true */

8.3.9

print_array

t3lib_div::print_array($array_in)

Erzeugt eine echo-Ausgabe des Arrays $array_in in Tabellenform.

190

8.3 Array-Funktionen Listing 8.68 Beispiel $arr = array( "beans" => array( "uid" => 56, "name" => "arabica", ), "milk" => false ); t3lib_div::print_array($arr);

Abbildung 8.1 Ausgabe von print_array()

8.3.10 removeArrayEntryByValue t3lib_div::removeArrayEntryByValue(array $array, $cmpValue)

Entfernt den Eintrag $cmpValue aus dem Array $array. Die Funktion gibt das geänderte Array zurück. Listing 8.69 Beispiel $arr = array( "beans" => array( "56" => "arabica", "57" => "robusta" ) ); $bestBeans = t3lib_div::removeArrayEntryByValue($arr, 'robusta'); /* Ergebnis in $bestBeans: Array ( [beans] => Array ( [56] => arabica ) ) */

8.3.11 slashArray t3lib_div::slashArray(array $arr,$cmd)

Mit $cmd=„add“ werden alle Werte im multidimensionalen Array $theArray maskiert. Die Umkehrfunktion erreicht man mit $cmd=„strip“.

191

8 Strings, Arrays und Umgebungsvariablen Listing 8.70 Beispiel $values = array('"Lorem"', '"ipsum"'); $values = t3lib_div:: slashArray($values, 'add'); /* Ergebnis in $values: Array ( [0] => \"Lorem\" [1] => \"ipsum\" ) */ $values = t3lib_div:: slashArray($values, 'strip'); /* Ergebnis in $values: Array ( [0] => "Lorem" [1] => "ipsum" ) */

8.3.12 view_array t3lib_div::view_array($array_in)

Erzeugt eine Ausgabe des Arrays $array_in in Tabellenform. Listing 8.71 Beispiel $arr = array( "beans" => array( "uid" => 56, "name" => "arabica", ), "milk" => false ); $content = t3lib_div::view_array($arr);

Abbildung 8.2 Ausgabe von view_array()

8.3.13 xml2array t3lib_div::xml2array($string,$NSprefix='',$reportDocTag=FALSE)

192

8.3 Array-Funktionen Liest den XML-String $string ein und konvertiert ihn in ein mehrdimensionales Array mit dem Inhalt der XML. Listing 8.72 Beispiel $xml = ' 56 arabica <milk type="boolean"> '; $arr = t3lib_div::xml2array($xml); /* Ergebnis in $arr: Array ( [beans] => Array ( [uid] => 56 [name] => arabica ) [milk] => ) */

8.3.14 xml2tree t3lib_div::xml2tree($string,$depth=999)

Liest den -String $string ein und konvertiert ihn in ein Array mit assoziativen Schlüsseln. Listing 8.73 Beispiel $xml = ' 56 arabica <milk type="boolean"> '; $arr = t3lib_div::xml2array($xml);

193

8 Strings, Arrays und Umgebungsvariablen

Abbildung 8.3 Inhalt von $arr

8.3.15 xmlGetHeaderAttribs t3lib_div::xmlGetHeaderAttribs($xmlData)

Liest den Header einer XML aus und gibt die Attribute als Array zurück. Listing 8.74 Beispiel $xml = ' 56 arabica <milk type="boolean"> '; $attr = t3lib_div::xmlGetHeaderAttribs($xml); /* Ergebnis in $attr: Array ( [version] => 1.0 [encoding] => utf-8 ) */

194

8.4 String-Funktionen in Frontend-Plug-ins

8.4

String-Funktionen in Frontend-Plug-ins

8.4.1

calcAge

$this->cObj->calcAge($seconds,$labels)

Berechnet die Anzahl der Sekunden $seconds in einen lesbaren Zeitwert. Soll der Zeitwert mit einer deutschen Einheit versehen werden, so muss $labels = „Minuten| Stunden| Tage| Jahre“ übergeben werden. Listing 8.75 Beispiel $tsAge = $this->cObj->calcAge( '36000', ' Minuten| Stunden| Tage| Jahre' ); /* Ergebnis in $tsAge: 10 Stunden */

8.4.2

checkEmail

$this->cObj->checkEmail($email)

Validiert den Syntax einer E-Mail-Adresse in $email. Listing 8.76 Beispiel $valid = $this->cObj->checkEmail('[email protected]'); /* Ergebnis in $valid: true */

8.4.3

codeString

$GLOBALS["TSFE"]->codeString($string, $decode=FALSE)

Funktion, um $string zu encodieren oder zu decodieren. Der Grad der Verschlüsselung ist sehr gering, genügt aber in jedem Fall, um Daten wie bspw. E-Mail-Adressen nicht unmittelbar lesbar zu machen.

195

8 Strings, Arrays und Umgebungsvariablen Listing 8.77 Beispiel $encodedString = $GLOBALS["TSFE"]->codeString('[email protected]'); $decodedString = $GLOBALS["TSFE"]->codeString($encodedString, true); /* Ergebnisse: $encodedString: 101bd24d67:Dld1QAoVBh1EWwc= $decodedString: [email protected] */

8.4.4

encryptEmail

$GLOBALS["TSFE"]->encryptEmail($string,$back=0)

Verschlüsselt E-Mail-Adressen für a-Tags, um sie vor Spam zu schützen. Mit $back=1 wird die Adresse wieder entschlüsselt. Listing 8.78 Beispiel $encrypt = $GLOBALS["TSFE"]->encryptEmail('mailto:[email protected]'); $decrypt = $GLOBALS["TSFE"]->encryptEmail($decrypt,1); /* Ergebnisse: $encrypt: $decrypt: */

8.4.5

HTMLcaseshift

$this->cObj->HTMLcaseshift($theValue, $case)

Ändert Groß- oder Kleinschreibung der Zeichen in $theValue, allerdings nur außerhalb von HTML-Tags. Gültige Werte für $case sind „upper“ und „lower“. Listing 8.79 Beispiel $string = $this->cObj->HTMLcaseshift('
Lorem Ipsum
', 'upper'); /* Ergebnis in $string:
LOREM IPSUM
*/

8.4.6

keywords

$this->cObj->keywords($content)

Bereinigt die in $content stehenden Keywords so, dass sie einheitlich mit Komma getrennt werden und in einer Zeile stehen.

196

8.4 String-Funktionen in Frontend-Plug-ins Listing 8.80 Beispiel $keywords = $this->cObj->keywords(' products coffee;beans milk '); /* Ergebnis in $keywords: products,coffee,beans,milk */

8.4.7

linebreaks

$this->cObj->linebreaks($string,$chars,$maxLines=0)

Trennt $string in einzelne Zeilen auf, die jeweils eine maximale Länge von $chars haben. Mit $maxLines kann die Anzahl der Zeilen begrenzt werden. Listing 8.81 Beispiel $string = ' 13;mickey;[email protected] 15;minni;[email protected] '; $lines = $this->cObj->linebreaks($string, 256); /* Ergebnis in $lines: Array ( [0] => [1] => 13;mickey;[email protected] [2] => 15;minni;[email protected] [3] => ) */

8.4.8

processParams

$this->cObj->processParams($params)

Liest einen String $params zeilenweise ein und speichert folgendes Format in ein Array: [parameter] = [value]

Listing 8.82 Beispiel $string = ' name = mickey maus email = [email protected] #kommentare wie in typoscript gewohnt ';

197

8 Strings, Arrays und Umgebungsvariablen $parameters = $this->cObj->processParams($string); /* Ergebnis in $parameters: Array ( [name] => mickey maus [email] => [email protected] ) */

8.4.9

uniqueHash

$GLOBALS["TSFE"]->uniqueHash($str='')

Erzeugt einen einmaligen md5-Hash. Listing 8.83 Beispiel $key = $GLOBALS["TSFE"]->uniqueHash(); /* Ergebnis in $key: deb72742bc23ca1c65bd2da2dd760634 */

8.4.10 URLqMark $this->cObj->URLqMark($url,$params)

Die GET-Parameter in $params werden an die $url angefügt. Hierbei wird überprüft, ob die URL bereits ein „?“ enthält, falls nicht, wird es eingefügt. Listing 8.84 Beispiel $url1 = $this->cObj->URLqMark( 'http://domain.tld/details.html','&offer=142' );

$url2 = $this->cObj->URLqMark( 'http://domain.tld/index.php?id=10','&offer=142' ); /* Ergebnisse: $url1: http://domain.tld/details.html?&offer=142 $url2: http://domain.tld/index.php?id=10&offer=142 */

198

9 9 9.1

Bilder

Einbinden $this->cObj->cImage($file,$conf)

Erzeugt einen img-Tag mit $file als src. Das Array $conf enthält die TypoScript-Konfigurationsparameter. Die Attribute „width“ und „height“ werden automatisch aus dem Bild ausgelesen und entsprechend gesetzt. Sollte das Bild nicht vorhanden sein, wird der gesamte IMG-Tag nicht erzeugt. Tabelle 9.1 Mögliche Werte für $conf Parameter

Typ

Bedeutung

params

string

Enthält zusätzliche Parameter, die dem IMG-Tag hinzugefügt werden. Beispiel: 'params' => 'onclick="zoom();"'

border

integer

Der Wert wird dem Attribut „border“ des IMG-Tags hinzugefügt. Es wird der DocType berücksichtigt, d.h., bei einem xhtml_strictDokument wird dieses Attribut nicht ausgegeben.

altText

string

Wird als Attribut „alt“ übernommen, zusätzlich auch als „title“, falls dieser Parameter nicht explizit gesetzt wird.

titleText

string

Wird als Attribut „title“ übernommen.

longdescURL

string

Wird als Attribut „longdesc“ übernommen.

(wrapper)

string

Zusätzlich können auch alle wrapper übergeben werden, falls das innerhalb einer Extension Sinn macht: linkWrap, imageLinkWrap, wrap und stdWrap

199

9 Bilder Listing 9.1 Beispiel $content = $this->cObj->cImage('fileadmin/user_upload/bilder/typo3.jpg', array( 'altText' => 'Typo3 Logo', 'linkWrap' => '' )); /* Ergebnis in $content: */

9.2

Bearbeiten Die hier vorgestellten Funktionen setzen eine lauffähige Installation der Pakete GraphicsMagick oder ImageMagick voraus, dies kann über das Install-Tool überprüft werden. Ausgangsdatei ist das TYPO3-Logo als tif-Datei (1595 x 665 Pixel, 3.1 MB). Alle Bearbeitungsschritte werden im Folgenden mit der Methode $this->cObj->IMAGE($conf)

durchgeführt, an die als Konfigurations-Array alle verfügbaren GifBuilder-Parameter (ausführlich dokumentiert in der TSref) übergeben werden können.

9.2.1

Bild umrechnen (fürs Web optimieren)

Listing 9.2 Beispiel $img = array( 'file' => 'fileadmin/images/typo3Logo.tif', 'altText' => 'Typo3 Logo' ); $image = $this->cObj->IMAGE($img); /* Ergebnis in $image: Typ3 Logo Dateigröße von "typo3temp/pics/42837d0d79.jpg": 49.75 kB */

200

9.2 Bearbeiten

9.2.2

Abmessungen ändern

Bei der Angabe von jeweils nur einer Option Breite / Höhe werden die Abmessungen des Bildes proportional geändert, bei Angabe beider Parameter werden diese eingehalten und das Bild ggf. verzerrt. Listing 9.3 Beispiel $img = array( 'file' => 'fileadmin/images/typo3Logo.tif', 'file.' => array( 'width' => '280' ), 'altText' => 'Typo3 Logo' ); $image = $this->cObj->IMAGE($img); /* Ergebnis in $image: Typo3 Logo Dateigröße von "typo3temp/pics/751ce379b5.jpg": 12.88 kB */ $image = $this->cObj->IMAGE($img); /* Ergebnis in $image: Typo3 Logo Dateigröße von "typo3temp/pics/751ce379b5.jpg": 12.88 kB */

9.2.3

Zuschneiden

Der GifBuilder arbeitet ähnlich wie grafische Bildbearbeitungsprogramme in Ebenen. Um also ein Bild zuzuschneiden, erstellt man zunächst eine Arbeitsfläche mit den gewünschten Abmessungen. In einer Ebene wird dann das Originalbild eingefügt und positioniert. Beim Speichern werden alle Ebenen wieder zusammengefügt, und man erhält eine zugeschnittene Version des Originalbildes mit den gewünschten Abmessungen, da „überstehende“ Bildteile nicht gespeichert werden. Genau in diesen Schritten ist auch das folgende Konfigurationsarray aufgebaut, um das Originalbild zuzuschneiden.

201

9 Bilder Listing 9.4 Beispiel $img = array( 'file' => 'GIFBUILDER', // entspricht Arbeitsfläche 'file.' => array( 'XY' => '280,90', // gewünschte Abmessung (Breite, Höhe) '10' => 'IMAGE', // neue Ebene "10" '10.' => array ( 'offset' => '0,-20', // Original-Bild um 20px nach oben geschoben 'file' => 'fileadmin/images/typo3Logo.tif', 'file.' => array( 'width' => '280' ) ) ), 'altText' => 'Typo3 Logo' ); $image = $this->cObj->IMAGE($img); /* Ergebnis in $image: Typo3 Logo Dateigröße von "typo3temp/GB/0f83b56f76.png": 2.57 kB */

9.2.4

Graustufen

Ebenso wie ein Bild kann auch ein Effekt in eine Ebene gelegt werden. Eine vollständige Liste der verfügbaren Effekte finden Sie in der aktuellen TSref. Listing 9.5 Beispiel $img = array( 'file' => 'GIFBUILDER', 'file.' => array( 'XY' => '280,[10.h]', '10' => 'IMAGE', '10.' => array( 'file' => 'fileadmin/images/typo3Logo.tif', 'file.' => array( 'width' => '280' ) ), '20' => 'EFFECT', '20.' => array( 'value' => 'gray' ) ), 'altText' => 'Typ3 Logo' ); $image = $this->cObj->IMAGE($img); /* Ergebnis in $image: Typ3 Logo Dateigröße von "typo3temp/GB/1d39efe23c.png": 2.63 kB */

202

9.3 Erzeugen

9.3

Erzeugen

9.3.1

Bild in bestehendes Bild einfügen

Die Herausforderung in diesem Beispiel ist, dass die Abmessungen der Arbeitsfläche nicht bekannt sind, sondern von dem „unteren“ Bild abhängen. Daher gibt es im GifBuilder sehr praktische Rechenoperationen, um auf dynamische Größen aus anderen Ebenen zugreifen zu können: [10.w] bezieht sich auf die Breite des Objekts 10, [10 h] entsprechend auf die Höhe. Das folgende Beispiel fügt über das Ausgangsbild ein TYPO3-Logo in die rechte obere Ecke ein. Listing 9.6 Beispiel $img = array( 'file' => 'GIFBUILDER', 'file.' => array( 'XY' => '[10.w],[10.h]', '10' => 'IMAGE', '10.' => array ( 'file' => 'fileadmin/images/typo3Logo.tif', 'file.' => array( 'width' => '300' ) ), '20' => 'IMAGE', '20.' => array ( 'offset' => '[10.w]-50,0', 'file' => 'fileadmin/images/typo3Logo.tif', 'file.' => array( 'width' => '50' ) ) ), 'altText' => 'Typo3 Logos' ); $image = $this->cObj->IMAGE($img);

Abbildung 9.1 Ergebnis in $image

203

9 Bilder

9.3.2

Text erzeugen

Text kann ebenso wie auch ein Bild in eine Ebene eingefügt werden, optional auch mit eigener TrueType-Schriftart. Auch die Höhe und Breite wird automatisch je nach Inhalt berechnet und steht als Variable im GifBuilder zur Verfügung. Beim Offset ist zu beachten, dass es sich immer auf die Zeilenlinie bezieht, also den unteren Rand der Schriftzeichen. Daher ist das Offset in y-Richtung immer mindestens gleich der Schriftgröße, ansonsten wird der Text oben abgeschnitten. Listing 9.7 Beispiel $img = array( 'file' => 'GIFBUILDER', 'file.' => array( 'XY' => '[10.w],[10.h]', '10' => 'TEXT', '10.' => array ( 'offset' => '0,15', 'text' => 'made by gifbuilder', 'fontFile' => 'fileadmin/fonts/typo3FreeFontRegular.ttf', 'fontSize' => '15', 'fontColor' => '#000000' ) ), 'altText' => 'made by gifbuilder' ); $image = $this->cObj->IMAGE($img);

Abbildung 9.2 Ergebnis in $image

204

10 10 AJAX im Frontend 10.1

eID Speziell für AJAX-Requests wurde in TYPO3 der eID-Mechanismus eingeführt. Er dient dazu, Daten an das Frontend zu übertragen, ohne den gesamten Frontend-RenderingProzess durchlaufen zu müssen. Der Aufruf erfolgt über einen eindeutigen Key bzw. eine "Extension-ID" index.php?eID=extkey

und gibt die Ausgabe des dahinter liegenden PHP-Skripts 1:1 wieder.

10.1.1

Konfiguration

Zunächst muss der eindeutige Key für den Aufruf definiert werden; er zeigt direkt auf das aufzurufende Skript. Da die eIDs aller Extensions innerhalb einer TYPO3-Installation nicht kollidieren dürfen, empfiehlt es sich, hier den Extension-Key mit einfließen zu lassen. Wenn diese Konfiguration in der Datei ext_localconf.php vorgenommen wird, steht die eID sofort nach Installation der Extension zur Verfügung. Listing 10.1 ext_localconf.php $TYPO3_CONF_VARS['FE']['eID_include']['extkey'] = 'EXT:extkey/inc/tx_extkey_eid.php';

205

10 AJAX im Frontend

10.1.2

Basisdatei

Folgende Basisdatei wird für jede eID empfohlen. Sie stellt sicher, dass das Skript nicht direkt aufgerufen werden kann, und bindet auf eine sehr ressourcenschonende Weise die TYPO3-Datenbank ein. Listing 10.2 eID-Basisdatei tx_extkey_eid.php

10.2

Zusammenspiel AJAX & eID Für das Zusammenspiel von AJAX und eID gibt es wahrscheinlich so viele Varianten wie Entwickler. Wir wollen Ihnen dennoch kurz einen bewährten Weg vorstellen, wie Sie Daten zuverlässig mittels eID in JavaScript bringen und von dort aus weiter verarbeiten können.

10.2.1

Aufbau der XML-Response

Listing 10.3 Beispiel 'hello world', 'content' => 'this is my first eID data' ); $ajax_return_data = t3lib_div::array2xml($res); header('Last-Modified: '.gmdate( "D, d M Y H:i:s" ).'GMT'); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); header('Content-Length: '.strlen($ajax_return_data)); header('Content-Type: text/xml'); echo $ajax_return_data; exit; ?>

206

10.2 Zusammenspiel AJAX & eID /* Ergebnis der Response: hello world this is my first eID data */

10.2.2

AJAX-Request und Verarbeitung

Listing 10.4 Beispiel-JavaScript (setzt prototype.js voraus) function eIDdata() { var myAjax = new Ajax.Request('index.php', method: 'get', parameters: 'eID=extkey', onComplete: process }); }

{

function process(data) { var xmldoc = data.responseXML; headline = xmldoc.getElementsByTagName('headline')[0].firstChild.nodeValue; content = xmldoc.getElementsByTagName('content')[0].firstChild.nodeValue; } /* Ergebnis in den Variablen headline und content: headline: 'hello world' content: 'this is my first eID data' */

207

11 11 Frontend-Plug-ins 11.1

Cache Der TYPO3-Cache ist ein elementarer Bestandteil des Frontend-Renderings und sollte generell NIE umgangen werden. Daher sind folgende Funktionen in diesem Kapitel mit großer Vorsicht zu genießen, bei falscher Anwendung können sie die Leistungsfähigkeit der TYPO3-Installation massiv beeinträchtigen!

11.1.1 cacheExpires $GLOBALS['TSFE']->cacheExpires;

Enthält den Timestamp, wann der Seitencache der aktuellen Seite das nächste Mal systemseitig gelöscht wird.

11.1.2 clearPageCacheContent $GLOBALS['TSFE']->clearPageCacheContent()

Löscht den Cache der aktuellen Seite. Listing 11.1 Beispiel $GLOBALS['TSFE']->clearPageCacheContent();

11.1.3 clearPageCacheContent_pidList $GLOBALS['TSFE']->clearPageCacheContent_pidList($pidList)

Löscht den Cache aller Seiten, die in $pidList angegeben sind (kommaseparierte Liste).

209

11 Frontend-Plug-ins Listing 11.2 Beispiel $GLOBALS['TSFE']->clearPageCacheContent_pidList('3,64,24');

11.1.4 get_cache_timeout $GLOBALS['TSFE']->get_cache_timeout()

Gibt die Anzahl der Sekunden zurück, wann der Cache der aktuellen Seite verfällt. Listing 11.3 Beispiel $cacheTimeout = $GLOBALS['TSFE']->get_cache_timeout(); /* Ergebnis in $cacheTimeout: 86400 */

11.1.5 set_cache_timeout_default $GLOBALS['TSFE']->set_cache_timeout_default($seconds)

Setzt den Cache Timeout für die aktuelle Seite. Listing 11.4 Beispiel: Cache Timeout von 60 Minuten $GLOBALS['TSFE']->set_cache_timeout_default(3600);

11.1.6 set_no_cache $GLOBALS['TSFE']->set_no_cache()

Deaktiviert den kompletten Cache der aktuellen Seite, sollte nur zu Testzwecken verwendet werden! Listing 11.5 Beispiel $GLOBALS['TSFE']->set_no_cache();

210

11.2 Content-Elemente

11.2

Content-Elemente

11.2.1 cImage $this->cObj->cImage($file,$conf)

Erzeugt einen img-Tag mit $file als src. Im Array $conf können die aus TypoScript bekannten Eigenschaften übergeben werden. Listing 11.6 Beispiel $content = $this->cObj->cImage('fileadmin/user_upload/bilder/typo3.jpg', array( 'altText' => 'Typo3 Logo', 'linkWrap' => '' )); /* Ergebnis in $content: */

11.2.2 cleanFormName $this->cObj->cleanFormName($name)

Entfernt nicht erlaubte Zeichen für die Attribute name und id in Formularen und Formularfeldern. Listing 11.7 Beispiel $name = $this->cObj->cleanFormName('contact form (b2b)'); /* Ergebnis in $name: contactformb2b */

11.2.3 cObjGet $this->cObj->cObjGet($setup,$addKey='')

Erzeugt mehrere Content-Elemente, die durch das TypoScript-Konfigurations-Array $setup definiert werden. Für jedes Element wird $this->cObj->cObjGetSingle() aufgerufen.

211

11 Frontend-Plug-ins Listing 11.8 Beispiel $conf['box']['10'] = 'IMAGE'; $conf['box']['10.']['file'] = 'fileadmin/user_upload/bilder/typo3.jpg'; $content = $this->cObj->cObjGet($conf['box']); /* Ergebnis in $content: */

11.2.4 cObjGetSingle $this->cObj->cObjGetSingle($name,$conf,$TSkey='__')

Erzeugt ein Content-Element. Als $name wird der Typ des Content-Elements übergeben (siehe TypoScript-Referenz) und in $conf das zugehörige TypoScript-KonfigurationsArray. Es können alle Content-Elemente erzeugt werden, die es in TypoScript gibt; bspw. TEXT, IMAGE, IMG_RESOURCE ... Listing 11.9 Beispiel $conf['file'] = 'fileadmin/user_upload/bilder/typo3.jpg'; $content = $this->cObj->cObjGetSingle('IMAGE', $conf); /* Ergebnis in $content: */

11.2.5 currentPageUrl $this->cObj->currentPageUrl($urlParameters=array(),$id=0)

Gibt die URL der aktuellen Seite zurück. Über den optionalen Parameter $id kann auch die URL einer bestimmten Seiten-ID ermittelt werden. Listing 11.10 Beispiel $url = $this->cObj->currentPageUrl(array('name' => 'test'), 40); /* Ergebnis in $url: path/to/script.html?name=test */

212

11.2 Content-Elemente

11.2.6 fileResource $this->cObj->fileResource($fName, $addParams='alt="" title=""')

Gibt den Inhalt einer Datei zurück. Sofern die Datei ein Bild ist, wird stattdessen ein imgTag erzeugt mit dem Bild als src. Listing 11.11 Beispiel $filecontent = $this->cObj->fileResource('fileadmin/user_upload/data.csv');

11.2.7 getImgResource $this->cObj->getImgResource($file,$fileArray)

Erzeugt eine Bildressource. $file kann entweder ein Pfad zu einem Bild sein oder der Text „GIFBUILDER“. $fileArray enthält das TypoScript-Konfigurations-Array für das zu erzeugende Bild. Listing 11.12 Beispiel $fileArray = $this->cObj->getImgResource ('fileadmin/user_upload/bilder/typo3.jpg', array()); /* Ergebnis in $fileArray: Array ( [0] => 310 [1] => 381 [2] => jpg [3] => fileadmin/user_upload/bilder/typo3.jpg [origFile] => fileadmin/user_upload/bilder/typo3.jpg [origFile_mtime] => 1248973934 ) */

11.2.8 getSlidePids $this->cObj->getSlidePids($pidList, $pidConf)

Gibt einer Liste aller Parent-Page-IDs zurück, die zu den Page-IDs in $pidList gehören. Optional kann in $pidConf ein stdWrap angegeben werden. Listing 11.13 Beispiel $parentIDs = $this->cObj->getSlidePids('33,40'); /* Ergebnis in $parentIDs: 7,30 */

213

11 Frontend-Plug-ins

11.2.9 gifBuilderTextBox $this->cObj->gifBuilderTextBox($gifbuilderConf, $conf, $text)

Erzeugt ein GifBuilder-Array für eine Textbox. Listing 11.14 Beispiel $gbConf = $this->cObj->gifBuilderTextBox( array('10' => 'TEXT'), array('tmplObjNumber' => '10'), 'hello world'); /* Ergebnis in $gbConf: Array ( [11] => WORKAREA [11.] => Array ( [set] => 0,0 ) [12] => TEXT [12.] => Array ( [text] => hello world ) )

*/

11.2.10

stdWrap

$this->cObj->stdWrap($content,$conf)

Entspricht der Funktion stdWrap aus der TypoSrcipt-Referenz. Die meisten der möglichen Funktionen sind ebenfalls aus der Klasse tslib_cObj und können auch direkt aufgerufen werden, oft ist es aber notwendig, mehrere Methoden nacheinander auf dasselbe Objekt anzuwenden, und hier ist der Aufruf innerhalb von stdWrap elegant und sehr zu empfehlen. Eine Übersicht aller verfügbaren Methoden führt an dieser Stelle zu weit, daher verweisen wir auf die jeweils aktuellste TSref. $content entspricht dem Parameter „value“ in TypoScript, $conf enthält das zugehörige Konfigurations-Array. Listing 11.15 Beispiele $content = $this->cObj->stdWrap('', array( 'filelist' => 'fileadmin/user_upload', 'wrap' => 'Dateien im Verzeichnis: |' )); /* Ergebnis in $content: Dateien im Verzeichnis: data.csv,data2.csv */

214

11.3 Links $content = $this->cObj->stdWrap('irgendein text', array( 'case' => 'upper', 'wrap' => '<span class="wrappedText">|' )); /* Ergebnis in $content: <span class="wrappedText">IRGENDEIN TEXT */

11.3

Links

11.3.1 baseUrlWrap $GLOBALS['TSFE']->baseUrlWrap($url)

Ergänzt $url um die baseUrl, sofern diese definiert ist und $url keine absolute URL ist. Listing 11.16 Beispiel $url = $GLOBALS['TSFE']->baseUrlWrap('path/to/script.html'); /* Ergebnis in $url: http://www.domain.com/path/to/script.html */

11.3.2 getMailTo $this->cObj->getMailTo($mailAddress,$linktxt,$initP='?')

Erzeugt das href-Attribut für einen „mailto:“-Link mit dem Schutz vor Spam-Crawlern. Ist „spamProtectEmailAddresses“ deaktiviert, so wird der klassische „mailto:“-Link erzeugt. Listing 11.17 Beispiel $link = $this->cObj->getMailTo( '[email protected]','[email protected]'); /* Ergebnis in $link: Array ( [0] => javascript:linkTo_UnCryptMailto('nbjmup+nz/nbjmAepnbjo/dpn'); [1] => my.mail(at)domain.com ) */

215

11 Frontend-Plug-ins $content = '
'.$link[1].''; /* Ergebnis in $content:

my.mail(at)domain.com

*/

11.3.3 getTypoLink $this->cObj>getTypoLink($label,$params,$urlParameters=array(),$target='')

Universell einsetzbar für Links auf Page-IDs, Mails oder auch externe URLs. Über $urlParameters können zusätzliche GET-Parameter definiert werden. Listing 11.18 Beispiel $linkPage = $this->cObj->getTypoLink('Link zur Seite 40',40,array()); $linkMail = $this->cObj->getTypoLink( '[email protected]', '[email protected]', array()); /* Ergebnisse: $linkPage: Link zur Seite 40 $linkMail: my.mail(at)domain.com

11.3.4 getTypoLink_URL $this->cObj->getTypoLink_URL($params,$urlParameters=array(),$target='')

Wie getTypoLink, erzeugt allerdings keinen a-Tag, sondern nur die URL. Listing 11.19 Beispiel $linkPage = $this->cObj->getTypoLink_URL(40, array()); /* Ergebnis in $linkPage: path/to/script.html */

216

11.3 Links

11.3.5 http_makelinks $this->cObj->http_makelinks($data,$conf)

Durchsucht den in $data übergebenen Text und erzeugt aus URLs Links. Die Links sind konfigurierbar über das TypoScript-Konfigurations-Array $conf. Listing 11.20 Beispiel $data = 'Unsere Referenzen finden Sie hier: http://www.domain.com/referenzen.html'; $data = $this->cObj->http_makelinks($data, array()); /* Ergebnis in $data: Unsere Referenzen finden Sie hier: www.domain.com */

11.3.6 imageLinkWrap $this->cObj->imageLinkWrap($string,$imageFile,$conf)

Erzeugt einen Link auf eine Bildvorschau des Bildes $imageFile. Der Link wird um das Element $string erzeugt, das bspw. das zugehörige Thumbnail enthalten kann. Ist wie im Beispiel JSwindow gesetzt, so öffnet die Bildvorschau ein Popup in den Abmessungen des Bildes. Listing 11.21 Beispiel $content = $this->cObj->imageLinkWrap( '', 'fileadmin/user_upload/bilder/typo3.jpg', array('enable' => 'true', 'JSwindow' => 'true' )); /* Ergebnis in $content:

217

11 Frontend-Plug-ins

11.3.7 mailto_makelinks $this->cObj->mailto_makelinks($data,$conf)

Durchsucht den Text in $data nach mailto: und erzeugt vor Spam geschützte mailto-Links. Listing 11.22 Beispiel $data = 'Bitte kontaktieren Sie uns per Mail: mailto:[email protected]'; $data = $this->cObj->mailto_makelinks($data, array()); /* Ergebnis in $data: Bitte kontaktieren Sie uns per Mail: my.mail(at)domain.com */

11.3.8 pi_getPageLink $this->pi_getPageLink($id,$target='',$urlParameters=array())

Gibt die URL zur Seiten-ID $id zurück. Listing 11.23 Beispiel $url = $this->pi_getPageLink(40); /* Ergebnis in $url: path/to/script.html */

11.3.9 pi_linkToPage $this->pi_linkToPage($str,$id,$target='',$urlParameters=array())

Erzeugt einen Link zur Seiten-ID $id um das Element $str. Über $urlParameters können zusätzliche GET-Parameter übergeben werden. Listing 11.24 Beispiel $content = $this->pi_linkToPage( 'zu unseren Referenzen', 40, '_blank', array('from' => 'home') ); /* Ergebnis in $content:

218

11.3 Links zu unseren Referenzen */

11.3.10

pi_linkTP

$this->pi_linkTP($str,$urlParameters=array(),$cache=0,$altPageId=0)

Erzeugt einen Link zur aktuellen Seite unter Berücksichtigung der zusätzlichen $urlParameters. Listing 11.25 Beispiel $content = $this->pi_linkTP('zu den nächsten 10 Ergebnissen', array(' tx_extkey_pi1[offset]' => '10')); /* Ergebnis in $content: zu den nächsten 10 Ergebnissen */

11.3.11

pi_linkTP_keepPIvars

$this->pi_linkTP_keepPIvars( $str,$overrulePIvars=array(),$cache=0,$clearAnyway=0,$altPageId=0 )

Wie pi_linkTP, es werden jedoch die aktuellen piVars beibehalten. Mittels $overrulePIvars können bestimmte piVars überschrieben bzw. neu gesetzt werden. Es werden ausschließlich standardkonforme piVars berücksichtigt, alle anderen GET-Parameter werden gelöscht. Listing 11.26 Beispiel unter url: http://domain.com/script.html?some=text&tx_extkey_pi1[offset]=10 $link = $this->pi_linkTP_keepPIvars('aktualisieren'); /* Ergebnis in $link: script.html?tx_extkey_pi1[offset]=10 */

11.3.12

pi_linkTP_keepPIvars_url

$this->pi_linkTP_keepPIvars_url( $overrulePIvars=array(),$cache=0,$clearAnyway=0,$altPageId=0 )

219

11 Frontend-Plug-ins Gleiche Funktionalität wie pi_linkTP_keepPIvars, jedoch entfällt der Parameter $str, da nur die URL zurückgegeben wird.

11.3.13

pi_openAtagHrefInJSwindow

$this->pi_openAtagHrefInJSwindow( $str,$winName='',$winParams='width=670,height=500,status=0,menubar=0, scrollbars=1,resizable=1' )

Ein in $str enthaltener a-Tag wird modifiziert, sodass href im Popup geöffnet wird mit den Eigenschaften $winParams. Alle Attribute außer href und onclick werden gelöscht. Listing 11.27 Beispiel $link = 'Bitte kontaktieren Sie uns.'; $content = $this->pi_openAtagHrefInJSwindow($link); /* Ergebnis in $content: Bitte kontaktieren Sie uns. */

11.3.14

prefixLocalAnchorsWithScript

$GLOBALS['TSFE']->prefixLocalAnchorsWithScript()

Ergänzt alle Anker innerhalb von $this->content mit der URL zum Skript. Listing 11.28 Beispiel aus nach oben wird nach oben

11.4

Listen Alle folgenden Listenfunktionen arbeiten mit reservierten Variablen und piVars, die nach Möglichkeit nicht verändert werden sollten: $this->piVars['pointer'] $this->piVars['mode'] $this->piVars['sword'] $this->piVars['sort']

220

11.4 Listen $this->internal['res_count'] $this->internal['dontLinkActivePage'] $this->internal['showFirstLast'] $this->internal['pagefloat'] $this->internal['showRange']

11.4.1 pi_list_browseresults $this->pi_list_browseresults( $showResultCount=1, $tableParams='', $wrapArr=array(), $pointerName='pointer', $hscText=TRUE )

Erzeugt eine Navigationszeile für die erzeugte Ergebnisliste, z.B. aus einer Datenbankabfrage. Listing 11.29 Beispiel $res = $GLOBALS["TYPO3_DB"]->exec_SELECTquery( "*", "fe_users" ,"deleted = '0'" ); $rows = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); $this->internal['res_count']=count($rows); $this->internal['results_at_a_time']=10; $this->internal['maxPages']=5; $content = $this->pi_list_browseresults(); /* Ergebnis in $content: Displaying results <span class="tx-t3ext-pi1-browsebox-strong">1 to 10 out of <span class="tx-t3ext-pi1-browsebox-strong">31 */

Zur individuellen Gestaltung können die jeweiligen Elemente über $wrapArr angepasst werden: Listing 11.30 Beispiel $wrapArr $wrapArr = array( 'browseBoxWrap' => '
|
', 'showResultsWrap' => '
|
', 'browseLinksWrap' => '
|
', 'showResultsNumbersWrap' => '<spanclass="showResultsWrap">|', 'disabledLinkWrap' => '<span class="disabledLinkWrap">|', 'inactiveLinkWrap' => '<span class="inactiveLinkWrap">|', 'activeLinkWrap' => '<span class="activeLinkWrap">|' );

221

11 Frontend-Plug-ins

11.4.2 pi_list_linkSingle $this->pi_list_linkSingle( $str,$uid,$cache=FALSE,$mergeArr=array(),$urlOnly=FALSE,$altPageId=0 )

Erzeugt einen Link um das Element $str zur Anzeige eines einzelnen Datensatzes, optional auch auf einer anderen Seite $altPageID, wobei $uid die jeweilige uid des Datensatzes darstellt. Listing 11.31 Beispiel $content = $this->pi_list_linkSingle('Details von Max',2); /* Ergebnis in $content: Details von Max

11.4.3 pi_list_makelist $this->pi_list_makelist($res,$tableParams='')

Aus dem Ergebnis einer Datenbankabfrage $res wird eine Liste erzeugt. Listing 11.32 Beispiel $res = $GLOBALS["TYPO3_DB"]->exec_SELECTquery( "*", "fe_users" ,"deleted = '0'" ); $content = $this->pi_list_makelist($res); /* Ergebnis in $content:
usernamename
max Max Müller
eva Eva Schmidt
*/

222

11.4 Listen

11.4.4 pi_list_modeSelector $this->pi_list_modeSelector($items=array(),$tableParams='')

Um verschiedene Listenmodi anzubieten, kann über diese Funktion ein einfacher Umschalter generiert werden. Die jeweiligen Modi sind in $items definiert. Listing 11.33 Beispiel $content = $this->pi_list_modeSelector( array(1 => 'flat', 2 => 'detailed') ); /*Ergebnis in $content: */

11.4.5 pi_list_searchBox $this->pi_list_searchBox($tableParams='')

Erzeugt ein einfaches Suchformular. Listing 11.34 Beispiel $content = $this->pi_list_searchBox(); /* Ergebnis in $content:
*/

223

11 Frontend-Plug-ins

11.5

JavaScript

11.5.1 additionalHeaderData $GLOBALS['TSFE']->additionalHeaderData;

In diesem Array können zusätzliche Header-Tags eingetragen werden, um z.B. externe JavaScript-Dateien einzubinden. Listing 11.35 Beispiel $js = '<script src="'. t3lib_extMgm::siteRelPath($this->extKey).'js/functions.js" type="text/javascript">'; $GLOBALS['TSFE']->additionalHeaderData[$this->extKey] = $js; /* Ergebnis Im html-Header: <script src="typo3conf/ext/extkey/js/functions.js" type="text/javascript"> */

11.5.2 additionalJavaScript $GLOBALS['TSFE']->additionalJavaScript;

Ein Array, das zusätzliche JavaScripts sammelt. Es wird als internes JavaScript in den HTML-Header geschrieben. Listing 11.36 Hinzufügen der Funktion preloader: $GLOBALS['TSFE']->additionalJavaScript[] = ' function preloader() { ... }'; /* Ergebnis: <script type="text/javascript"> /* /*]]>*/ */

224

11.5 JavaScript

11.5.3 JSeventFuncCalls $GLOBALS['TSFE']->JSeventFuncCalls;

In dieses Array kann man JavaScript-Funktionen zu folgenden Ereignissen eintragen: onmousemove onmouseup onmousemove onkeydown onkeyup onkeypress onload onunload

Hierbei ist gerade bei Mouse- und Key-Ereignissen darauf zu achten, dass man nicht mit anderen Extensions kollidiert. Um ein Überschreiben der Einträge durch andere Extensions zu verhindern, empfiehlt es sich, als Array-Key den Extension-Key zu verwenden. Die Einträge werden nur bei gecacheten Extensions berücksichtigt. Listing 11.37 Beispiel für onload $GLOBALS['TSFE']->JSeventFuncCalls['onload'][$this->extKey] = 'preloadPictures();'; /* Ergebnis: Der body-Tag wird erweitert: Der eigentliche Funktionsaufruf wird in der von Typo3 dynamisch erzeugten JavaScript-Datei eingetragen: function T3_onloadWrapper(e){preloadPictures();} document.onload=T3_onloadWrapper;

11.5.4 minifyJavaScript t3lib_div::minifyJavaScript($script, &$error = '')

Minimiert das in $script übergebene JavaScript. Listing 11.38 Beispiel $script = ' for (var i = 0; i < kontakte.length; i++) { document.write("
Kontakt " + (i + 1) + "<\/dt>"); for (var Eigenschaft in kontakt[i]) document.write("
" + Eigenschaft + ": " + kontakt[i][Eigenschaft] + "<\/dd>");

225

11 Frontend-Plug-ins document.write("<\/dl>"); }'; $minScript = t3lib_div::minifyJavaScript($script); /* Ergebnis in $minScript: for(var i=0;i
Kontakt "+(i+1)+"<\/dt>");for(var Eigenschaft in kontakt[i]) document.write("
"+Eigenschaft+": "+kontakt[i][Eigenschaft]+"<\/dd>");document.write("<\/dl>");} */

11.5.5 quoteJSvalue t3lib_div::quoteJSvalue($value, $inScriptTags = false)

Formatiert den String $value um, sodass er direkt als JavaScript-Variablenwert verwendet werden kann. Listing 11.39 Beispiel $jsValue = "Kaffeebohne 'Arabica'"; $script = t3lib_div::quoteJSvalue($jsValue); /* Ergebnis in $script: 'Müller\'s Kaffeebohne \'Arabica\'' */

11.5.6 rawUrlEncodeJS t3lib_div::rawUrlEncodeJS($str)

Gleiche Funktionalität wie die PHP-Funktion rawurlencode(), allerdings bleiben Leerzeichen erhalten. Listing 11.40 Beispiel $jsValue = "Kaffeebohne 'Arabica'"; $script = t3lib_div::rawUrlEncodeJS($jsValue); /* Ergebnis in $script: Kaffeebohne %27Arabica%27 */

226

11.5 JavaScript

11.5.7 setJS $GLOBALS['TSFE']->setJS($key,$content='')

Fügt $content zum additionalJavaScript-Array hinzu. Für $key sollte möglichst der jeweilige Extension-Key verwendet werden. Der Inhalt des additionalJavaScript-Arrays wird beim Rendern in den HTML-Head geschrieben. Listing 11.41 Beispiel im HTML-Head: $GLOBALS['TSFE']->setJS('myExtKey', ' function showContact(id) { doSomething(); } '); /* Ergebnis: <script type="text/javascript"> /* /*]]>*/ */

11.5.8 wrapJS t3lib_div::wrapJS($string, $linebreak=TRUE)

Schreibt den Inhalt von $string in ein XHTML-konformes script-Tag. $linebreak ist standardmäßig gesetzt und fügt vor und nach dem script-Tag eine neue Zeile ein. Listing 11.42 Beispiel: $content = t3lib_div::wrapJS('doSomething();'); /* Ergebnis in $content: <script type="text/javascript"> /**/ */

227

11 Frontend-Plug-ins

11.6

CSS

11.6.1 additionalCSS $GLOBALS['TSFE']->additionalCSS;

Ein Array, das zusätzliche Formatierungen sammelt. Es wird als internes Stylesheet in den HTML-Header geschrieben. Listing 11.43 Einfügen der Klasse „newCssClass“ $GLOBALS['TSFE']->additionalCSS[] = '.newCssClass {font-size:1.2em; color:#990000; }'; /* Ergebnis: <style type="text/css"> /* /*]]>*/ */

11.6.2 additionalHeaderData $GLOBALS['TSFE']->additionalHeaderData;

In diesem Array können zusätzliche Header-Tags eingetragen werden, um z.B. externe CSS-Dateien einzubinden. Listing 11.44 Beispiel $css = ''; $GLOBALS['TSFE']->additionalHeaderData[$this->extKey] = $css; /* Ergebnis im html-Header: */

228

11.6 CSS

11.6.3 pi_getClassName $this->pi_getClassName($class)

Generiert einen einheitlichen class-Name unter der Berücksichtigung von $this->prefixId. Listing 11.45 Beispiel: $className = $this->pi_getClassName('active'); /* Ergebnis in $className: tx-extkey-pi1-active */

11.6.4 pi_setClassStyle $this->pi_setClassStyle($class,$data,$selector='')

Schreibt die CSS-Eigenschaften $data XHTML-konform in den HTML-Head, der className wird hierbei mittels $this->pi_getClassName generiert. Über den Parameter $selector kann die Klasse für ein bestimmtes Tag eingeschränkt werden. Listing 11.46 Beispiel: $this->pi_setClassStyle('active', 'color:red;border:1px solid;fontweight:bold;'); $this->pi_setClassStyle('inactive', 'color:grey;border:1px solid grey;font-weight:normal;', 'a'); /* Ergebnis im HTML-head: <style type="text/css"> /* /*]]>*/ */

11.6.5 setCSS $GLOBALS['TSFE']->setCSS($key,$content)

Fügt $content zum additionalCSS-Array hinzu. Für $key sollte möglichst der jeweilige Extension-Key verwendet werden. Der Inhalt des additionalCSS-Arrays wird beim Rendern in den HTML-Head geschrieben.

229

11 Frontend-Plug-ins Listing 11.47 Beispiel $GLOBALS['TSFE']->setCSS('extkey', ' .tx-extkey-pi1-active {color:red;border:1px solid;font-weight:bold;} '); /* Ergebnis im HTML-Head: <style type="text/css"> /* /*]]>*/ */

11.7

Mehrsprachigkeit

11.7.1 getLLL $GLOBALS['TSFE']->getLLL($index, &$LOCAL_LANG)

Der Schlüssel $index wird im locallang-Array gesucht, und die entsprechende Bezeichnung der aktuellen Sprache wird zurückgegeben. Ist $index in der aktuellen Sprache nicht definiert, springt die Funktion auf den languageKey „default“ zurück. Üblicherweise verwendet man die locallang.xml im Verzeichnis des jeweiligen Frontend-Plug-ins – hierfür wird als zweiter Parameter $this->LOCAL_LANG gesetzt. Listing 11.48 Beispiel $label = $GLOBALS['TSFE']->getLLL('lastname', $this->LOCAL_LANG); /* Ergebnis in $label: Nachname */

11.7.2 pi_getLL $this->pi_getLL($key,$alt='',$hsc=FALSE)

Der Schlüssel $key wird im locallang-Array gesucht, und die entsprechende Bezeichnung der aktuellen Sprache wird zurückgegeben. Ist $key in der aktuellen Sprache nicht definiert, springt die Funktion auf den languageKey „default“ zurück. Zusätzlich kann mit $alt eine Alternativbezeichnung übergeben werden, falls der Schlüssel nicht in der local-

230

11.7 Mehrsprachigkeit lang.xml enthalten ist. Falls $hsc gesetzt ist, wird der Rückgabewert noch durch htmlspecialchars bearbeitet. Listing 11.49 Beispiel $content. = $this->pi_getLL('lastname', '[Nachname]').': '; $content. = $this->pi_getLL('forename', '[Vorname]') .': '; //nicht in locallang.xml /* Ergebnis in $content: Nachname: [Vorname]: */

11.7.3 readLLfile $GLOBALS['TSFE']->readLLfile($fileRef)

Liest eine Sprachdatei mit der aktuell im Frontend eingestellten Sprache ein und gibt ein $LOCAL_LANG-Array zurück. Listing 11.50 Beispiel /* Ausschnitt aus locallang.xml: */ $langValues = $GLOBALS['TSFE']->readLLfile('typo3conf/ext/extkey/locallang.xml'); /* Ergebnis in $langValues: Array ( [default] => Array ( [tx_extkey_data] => Daten [tx_extkey_data.content] => Inhalt [flexform.data.content.title] => Titel ) ) */

231

11 Frontend-Plug-ins

11.8

Umgebungsvariablen und allgemeine Funktionen

11.8.1 absRefPrefix $GLOBALS['TSFE']->absRefPrefix;

Wenn dieser Parameter gesetzt ist, werden alle Dateiverweise innerhalb der Ordner media, typo3conf/ext, typo3/contrib und fileadmin als absoluter Pfad angegeben.

Listing 11.51 Beispiel mit Wert 'http://www.domain.tld/' /* vorher: */ $GLOBALS['TSFE']->absRefPrefix = 'http://www.domain.tld/'; /* Ergebnis:

11.8.2 additionalHeaderData $GLOBALS['TSFE']->additionalHeaderData;

In dieses Array werden alle zusätzlichen Header-Tags geschrieben. Die Ausgabe erfolgt 1:1. Listing 11.52 Beispiel keywords $GLOBALS['TSFE']->additionalHeaderData[] = '<meta name="keywords" content="Typo3,Extensions,additionalHeaderData" />'; /* Ergebnis: <meta name="keywords" content="Typo3,Extensions,additionalHeaderData" /> */

11.8.3 all $GLOBALS['TSFE']->all;

Dieses Array wird von TYPO3 zur Einbindung der Templates genutzt, kann jedoch sehr gut genutzt werden, um Informationen über die aktuelle Rootline auszulesen. Es sind die

232

11.8 Umgebungsvariablen und allgemeine Funktionen vollständigen Inhalte der Tabelle pages für alle Seiten innerhalb der aktuellen Rootline enthalten. Listing 11.53 Ausgabe des aktuellen Seitenpfades foreach ($GLOBALS['TSFE']->all['rootLine'] as $currPage) $pageTitle[] = $currPage['title']; $content.= implode(' -> ',$pageTitle); /* Ergebnis: Seitentitle Level 0 -> Seitentitel Level 1 -> Seitentitel Level 2 */

11.8.4 anchorPrefix $GLOBALS['TSFE']->anchorPrefix;

Wenn Speaking URLs genutzt werden, enthält $GLOBALS['TSFE']->anchorPrefix den Seitenpfad ab der baseUrl und kann ebenso auch verändert bzw. angepasst werden.

11.8.5 applicationData $GLOBALS['TSFE']->applicationData;

Dient als Speicher- und Übergabeort von Extension-Variablen. Als Key sollte immer der String 'tx_[ExtensionKey]' verwendet werden. Listing 11.54 Setzen der Variable var1 der Extension myext $GLOBALS['TSFE']->applicationData['tx_myext']['var1'] = 'wert'; /* Ergebnis: Array ( [tx_myext] => Array ( [var1] => wert ) ) */

11.8.6 ATagParams $GLOBALS['TSFE']->ATagParams;

Fügt automatisch generierten Links die als Wert enthaltenen Parameter an. Listing 11.55 Beispiel $GLOBALS['TSFE']->ATagParams = 'class="newCssClass"'; $content.=$this->pi_linkToPage('home','5');

233

11 Frontend-Plug-ins /* Ergebnis: home */

11.8.7 baseUrl $GLOBALS['TSFE']->baseUrl;

Enthält die in TypoScript konfigurierte config.baseURL.

11.8.8 baseUrlWrap $GLOBALS['TSFE']->baseUrlWrap($url);

Sofern baseUrl gesetzt ist, wird diese vor $url angefügt. Listing 11.56 Beispiel $url = $GLOBALS['TSFE']->baseUrlWrap($this->pi_getPageLink('24')); /* Ergebnis in $url: http://domain.com/path/to/site.html */

11.8.9 beUserLogin $GLOBALS['TSFE']->beUserLogin;

Hat den Wert „1“, wenn in der gleichen Session ein Backend-User eingeloggt ist, ansonsten leer.

11.8.10

clientInfo

$GLOBALS['TSFE']->clientInfo;

Enthält Basisinformationen über den Browser und das Betriebssystem des Clients. Listing 11.57 Beispiel $client = $GLOBALS['TSFE']->clientInfo; /* Ergebnis in $client: Array ( [BROWSER] => net

234

11.8 Umgebungsvariablen und allgemeine Funktionen [VERSION] => 5 [SYSTEM] => win [FORMSTYLE] => 1 )

11.8.11

content

$GLOBALS['TSFE']->content;

Enthält den gesamten gerenderten Content (HTML-Quelltext) bis zum eingefügten Frontend-Plug-in. Listing 11.58 Beispiel $previousContent = $GLOBALS['TSFE']->content; /* Ausschnitt aus $previousContent: […]

Index



11.8.12

defaultBodyTag

$GLOBALS['TSFE']->defaultBodyTag;

Enthält den aktuellen Body-Tag. Listing 11.59 Beispiel $tag = $GLOBALS['TSFE']->defaultBodyTag; /* Ergebnis in $tag: */

11.8.13

domainStartPage

$GLOBALS['TSFE']->domainStartPage;

Enthält die Seiten-ID, an der das Flag „Is root of website“ gesetzt ist.

235

11 Frontend-Plug-ins Listing 11.60 Beispiel $rootID = $GLOBALS['TSFE']->domainStartPage; /* Ergebnis in $rootID: 5 */

11.8.14

extTarget

$GLOBALS['TSFE']->extTarget;

Das konfigurierte Ziel für externe Links. Listing 11.61 Beispiel $target = $GLOBALS['TSFE']->extTarget; /* Ergebnis in $target: _top */

11.8.15

fePreview

$GLOBALS['TSFE']->fePreview;

Kennzeichen, dass die Seitenvorschau aktiv ist. 0 = keine Seitenvorschau 1 = Seitenvorschau in der Liveumgebung 2 = Seitenvorschau in einem Workspace Listing 11.62 Beispiel $preview = $GLOBALS['TSFE']->fePreview; /* Ergebnis in $preview: 2 */

11.8.16

id

$GLOBALS['TSFE']->id;

Die Seiten-ID der aktuellen Seite.

236

11.8 Umgebungsvariablen und allgemeine Funktionen Listing 11.63 Beispiel $pid = $GLOBALS['TSFE']->id; /* Ergebnis in $pid: 32 */

11.8.17

imagesOnPage

$GLOBALS['TSFE']->imagesOnPage;

Enthält ein Array aller Bilder der jeweiligen Seite, sofern sie durch TYPO3 eingefügt wurden. Listing 11.64 Beispiel $pictures = $GLOBALS['TSFE']->imagesOnPage; /* Ergebnis in $pictures: Array ( [0] => fileadmin/user_upload/bilder/bild1.jpg [1] => fileadmin/user_upload/bilder/bild2.jpg [2] => fileadmin/user_upload/bilder/bild3.jpg ) */

11.8.18

intTarget

$GLOBALS['TSFE']->intTarget;

Das konfigurierte Ziel für interne Links. Listing 11.65 Beispiel $target = $GLOBALS['TSFE']->intTarget; /* Ergebnis in $target: _main */

11.8.19

lang

$GLOBALS['TSFE']->lang;

Die aktuell gesetzte Sprache im Frontend.

237

11 Frontend-Plug-ins Listing 11.66 Beispiel $langKey = $GLOBALS['TSFE']->lang; /* Ergebnis in $langKey: de */

11.8.20

lastImageInfo

$GLOBALS['TSFE']->lastImageInfo;

Enthält detaillierte Informationen über das zuletzt von TYPO3 gerenderte Bild. Listing 11.67 Beispiel $imgDetails = $GLOBALS["TSFE"]->lastImageInfo; /* Ergebnis in $imgDetails: Array ( [0] => [1] => [2] => [3] =>

310 // Breite 381 // Höhe jpg fileadmin/user_upload/bilder/bild1.jpg //abweichend, falls das Bild umgerechnet wurde [origFile] => fileadmin/user_upload/bilder/bild1.jpg [origFile_mtime] => 1248973934

)

11.8.21

loginUser

$GLOBALS['TSFE']->loginUser;

Kennzeichen, ob ein Frontend-User angemeldet ist. Listing 11.68 Beispiel $private = $GLOBALS['TSFE']->loginUser; /* Ergebnis in $private: 0 = kein User angemeldet 1 = ein Frontend-User ist angemeldet */

11.8.22

no_cache

$GLOBALS['TSFE']->no_cache;

Wird dieser Wert auf „true“ gesetzt, wird die gesamte aktuelle Seite nicht gecachet!

238

11.8 Umgebungsvariablen und allgemeine Funktionen Listing 11.69 Beispiel $GLOBALS['TSFE']->no_cache = true;

11.8.23

page

$GLOBALS['TSFE']->page;

Enthält ein Array mit dem gesamten Datensatz der aktuellen Seite aus der Tabelle pages. Listing 11.70 Beispiel $currentPage = $GLOBALS['TSFE']->page; /* Ausschnitt aus $currentPage: Array ( [uid] => 32 [pid] => 7 [tstamp] => 1255862705 [sorting] => 512 [deleted] => 0 [crdate] => 1248936137 [hidden] => 0 [title] => Seitentitel [doktype] => 1 [nav_hide] => 0 [...] => ... ) */

11.8.24

printError

$GLOBALS['TSFE']->printError($header,$label)

Erzeugt eine TYPO3-Fehlermeldung, die noch vor dem Rendern der Seite durch print_r ausgegeben wird. Listing 11.71 Beispiel $GLOBALS['TSFE']->printError( 'Leider konnte Ihre Anfrage nicht bearbeitet werden, versuchen Sie es später noch einmal.', 'Dienst derzeit nicht erreichbar'); die();

239

11 Frontend-Plug-ins

Abbildung 11.1 Ergebnis von printError()

11.8.25

rootLine

$GLOBALS['TSFE']->rootLine;

Enthält ein Abbild der aktuellen rootLine, inklusive einem Ausschnitt der Datensätze der enthaltenen Seiten aus der Tabelle pages. Listing 11.72 Beispiel $rootline = $GLOBALS['TSFE']->rootLine; /* Auszug aus $rootline: Array ( [2] => Array ( [pid] => 7 [uid] => 32 […] => … ) [1] => Array ( [pid] => 5 [uid] => 7 […] => … ) [0] => Array ( [pid] => 0 [uid] => 5 […] => … ) ) */

11.8.26

siteScript

$GLOBALS['TSFE']->siteScript;

Der URL-Pfad zur aktuellen Seite.

240

11.8 Umgebungsvariablen und allgemeine Funktionen Listing 11.73 Beispiel $path = $GLOBALS['TSFE']->siteScript; /* Ergebnis in $path: path/to/script/ */

11.8.27

tmpl->config, tmpl->setup

$GLOBALS['TSFE']->tmpl->config; $GLOBALS['TSFE']->tmpl->setup;

Diese beiden Arrays enthalten den gesamten TypoScript-Config- bzw. -Setup-Inhalt der aktuellen Seite.

11.8.28

type

$GLOBALS['TSFE']->type;

Der aktuelle Seitentyp (typeNum).

11.8.29

TYPO3_CONF_VARS

$GLOBALS['TSFE']->TYPO3_CONF_VARS;

In diesem Array dürfte so ziemlich alles zu finden sein, was auch nur im Entferntesten konfigurierbar ist. Ein Blick auf die root-Keys sagt wahrscheinlich schon alles:

„ „ „ „ „ „ „ „ „ „

GFX SYS EXT BE FE MODS USER SC_OPTIONS EXTCONF SVCONF

Listing 11.74 Beispiel: Versionsnummer des installierten RTE $version = $GLOBALS['TSFE']->TYPO3_CONF_VARS['EXTCONF']['rtehtmlarea']['version'];

241

11 Frontend-Plug-ins /* Ergebnis in $version: 1.7.11 */

11.8.30

uniqueString

$GLOBALS['TSFE']->uniqueString;

Enthält einen einmaligen String, wird bei jedem Rendern der Seite neu generiert. Listing 11.75 Beispiel $string = $GLOBALS['TSFE']->uniqueString; /* Ergebnis in $string ee3350e52eed1f33a5299f6b28a9f696 */

11.9

Konfiguration mit Flexforms Um das eingefügte Frontend-Plug-in möglichst komfortabel konfigurieren zu können, empfiehlt es sich, hierfür eine Flexform anzulegen. Die Formulardaten werden dann direkt in der Tabelle tt_content abgespeichert und gelten individuell für jede Content-ID.

11.9.1 Erstellen einer Plug-in_Flexform Zunächst erstellt man im Root-Verzeichnis der Extension die Datei: flexform_ds.xml

Diese Datei enthält die Datenstruktur der Flexform. Hier zunächst ein Beispiel, ausführlicher wird der Aufbau von Flexforms noch in Kapitel 8 behandelt. Listing 11.76 Beispiel flexform_ds.xml <meta> 1 <sheetTitle>Konfiguration array <el>

242

11.9 Konfiguration mit Flexforms input <size>30
group file jpg,gif,png <max_size>2000 uploads/tx_extkey <show_thumbs>1 <size>1 <minitems>0 <maxitems>1


So sieht diese Beispiel-Flexform dann im Backend aus:

Abbildung 11.2 Ausgabe von flexform_ds.xml

11.9.2 Auslesen der Formulardaten Um die Konfigurationsdaten auszulesen, genügt es, den Flexform-Parser zu initialisieren, danach kann auf die Felder direkt zugegriffen werden. Listing 11.77 Beispiel, um Flexform auszulesen $this->pi_initPIflexForm(); $ffValues = $this->cObj->data['pi_flexform']; $headline = $this->pi_getFFvalue($ffValues, 'headline'); $background = $this->pi_getFFvalue($ffValues, 'background');

243

11 Frontend-Plug-ins

11.10 Konfiguration mit TypoScript Für viele Zwecke ist es praktisch, ein Frontend-Plug-in über TypoScript konfigurieren zu können. Jedes Plug-in hat einen definierten TypoScript-Pfad, auf den in der Extension direkt zugegriffen werden kann. Listing 11.78 TypoScript („extkey“ entspricht dem Extension-Key ohne Unterstriche) plugin.tx_extkey_pi1.color = #676767

Listing 11.79 Auslesen im Frontend-Plug-in (hier pi1) $color $this->config['color']; /* Ergebnis in $color: #676767 */

11.11 Konfiguration im Extension-Manager Für Basiseinstellungen einer Extension können Formulare auch direkt im ExtensionManager auf der Übersichtsseite der Extension eingefügt werden. Hierzu muss im Root der Extension die Datei ext_conf_template.txt erstellt werden. Listing 11.80 Inhalt von ext_conf_template.txt # cat=basic/enable; type=boolean; label= Enable Function: Special feature configurationParameter = 0

Listing 11.81 Auslesen im Frontend-Plug-in $configuration = unserialize( $GLOBALS['TSFE']->TYPO3_CONF_VARS['EXT']['extConf'][$this->extKey] ); /* Ergebnis in $configuration: Array ( [configurationParameter] => 1 ) */

244

12 12 Frontend-User & Sessions 12.1 Frontend-User

12.1.1 Daten des aktuell angemeldeten Users $GLOBALS['TSFE']->fe_user->user

Dieses Array enthält den gesamten Datensatz des angemeldeten Users aus der Tabelle fe_users. Ist aktuell kein User angemeldet, ist das Array leer. Zusätzlich enthält das Array Daten über die aktuelle Session. Listing 12.1 Beispiel $userInfo = $GLOBALS['TSFE']->fe_user->user; /* Ausschnitt aus $userInfo: Array ( [ses_id] => 0387cbbba202806e3784bcc74b647159 [ses_name] => fe_typo_user [ses_iplock] => 127.0 [ses_hashlock] => 9958639 [ses_userid] => 2 [ses_tstamp] => 1256238446 [ses_data] => [ses_permanent] => 1 [uid] => 2 [pid] => 6 [tstamp] => 1249765982 [username] => max […] => … [lastlogin] => 1256237960 [is_online] => 1256237960 )

245

12 Frontend-User & Sessions

12.2 Sessions

12.2.1 Daten speichern Session-Daten können entweder temporär oder permanent gespeichert werden. Temporär gespeicherte Daten werden auf einen Session-Key bezogen in der Datenbank abgelegt, der Session-Key wird beim User in einem Session-Cookie gespeichert. Permanent gespeicherte Daten erfordern einen eingeloggten Frontend-User und werden ebenfalls in der Datenbank gespeichert. Die Daten werden einer Art „Variablennamen“ zugeordnet, damit verschiedene Extensions nicht kollidieren, empfiehlt es sich, den Extension-Key als Präfix zu verwenden. Listing 12.2 Beispiel: temporäre Daten speichern $GLOBALS['TSFE']->fe_user->setKey("ses","name",$value);

Listing 12.3 Beispiel: permanente Daten speichern $GLOBALS['TSFE']->fe_user->setKey("user","name",$value);

12.2.2 Daten auslesen Auf gleichem Weg lassen sich die Daten auch wieder auslesen. Listing 12.4 Beispiel $value = $GLOBALS["TSFE"]->fe_user->getKey("user","name");

12.2.3 Warenkorb aufbauen $GLOBALS["TSFE"]->fe_user->record_registration($recs, $maxSizeOfSessionData=0)

Mit der Funktion record_registration() kann man auf sehr effektive Weise eine WarenkorbFunktionalität realisieren. Der Warenkorb wird generell nur als temporär abgespeichert. $recs enthält das Array der Datensätze, die zum Warenkorb hinzugefügt werden sollen. Der Aufbau des Arrays ist folgendermaßen festgelegt: array( '[tabelle]' => array( '[uid]' => 'bezeichnung' ) )

Es können auch mehrere uids gleichzeitig hinzugefügt werden.

246

12.2 Sessions Ausgelesen wird der Warenkorb über: $GLOBALS["TSFE"]->fe_user->getKey('ses', 'recs');

Ein spezieller Aufruf mit dem Array-Key "clear_all" löscht den gesamten Inhalt. Listing 12.5 Beispiel: Warenkorb füllen und auslesen // Produkt hinzufügen: $newItems['tx_extkey_data']['67'] = 'Kaffee Arabica, Espresso Bohne 1kg'; $GLOBALS["TSFE"]->fe_user->record_registration($newItems); // Warenkorb auslesen: $basket = $GLOBALS["TSFE"]->fe_user->getKey('ses', 'recs'); /* Ergebnis in $basket: Array ( [tx_extkey_data] => Array ( [67] => Kaffee Arabica, Espresso Bohne 1kg ) ) */

Listing 12.6 Beispiel: Warenkorb löschen $GLOBALS["TSFE"]->fe_user->record_registration( array('clear_all' => '1'));

247

13 13 Backend & Services 13.1 Eigene Flexforms Als Basis für dieses Kapitel wird die Struktur eines Flexform-Feldes verwendet, das mit dem Kickstarter (Version > 0.4.0) erstellt wurde. Beim Anlegen oder Erweitern einer Tabelle kann man hier einem neuen Feld den Typ „Flex“ zuweisen.Für dieses Feld wird beim Erzeugen der Extension eine XML-Datei im Root der Extension erstellt, etwa so: flexform_tx_extkey_data_content.xml

Die Datei wird der Tabelle im tca zugeordnet, in unserem Beispiel ist es das Feld "content" in der Tabelle "data": … 'content' => array ( 'exclude' => 0, 'label' => 'LLL:EXT:extkey/locallang_db.xml:tx_extkey_data.content', 'config' => array ( 'type' => 'flex', 'ds' => array ( 'default' => 'FILE:EXT:extkey/flexform_tx_extkey_data_content.xml', ), ) ), …

Listing 13.1 Inhalt von flexform_tx_extkey_data_content.xml <meta> 1 array <el> <xmlTitle>

249

13 Backend & Services input <size>30
<xmlText> text <size>48


Im Backend sieht das Kickstarter-Formular dann so aus:

Abbildung 13.1 Flexform-Formular mit dem Kickstarter erstellt

13.1.1 Mehrsprachigkeit Alle Felder können wie auch in der tca üblich mehrsprachig hinterlegt werden. Um die Übersicht in der locallang_db.xml nicht zu verlieren, empfiehlt es sich, hierfür eine eigene Sprachdatei anzulegen: locallang_ff_data_content.xml

Diese hat die gleiche Struktur wie die Datei locallang_db.xml. Nun können Felder über folgenden String mehrsprachig hinterlegt werden. Listing 13.2 Auszug aus flexform_tx_extkey_data_content.xml input <size>30

250

13.1 Eigene Flexforms Listing 13.3 Beispiel für locallang_ff_data_content.xml <meta type="array"> flexform <description>Language labels for flexform in 'tx_extkey_data.content'

13.1.2 Tabs Um dem Formular Tabs hinzuzufügen, wird eine zusätzliche Hierarchie außen um das ROOT-Element hinzugefügt. Alle Tabs werden im Element <sheets> gesammelt, die Bezeichnung der jeweiligen Tab-Elemente kann beliebig sein. Beschriftet werden die Tabs über das Element <sheetTitle>. Listing 13.4 Beispiel <meta> 1 <sheets> <sheetTitle> LLL:EXT:extkey/locallang_ff_data_content.xml:title.tab1 array <el> ... <sheetTitle> LLL:EXT:extkey/locallang_ff_data_content.xml:title.tab2 array <el> ...

251

13 Backend & Services Listing 13.5 Zugehörige locallang_ff_data_content.xml <meta type="array"> flexform <description>Language labels for flexform in 'tx_extkey_data.content'

Im deutschsprachigen Backend sieht das Formular inzwischen so aus:

Abbildung 13.2 Flexform mit Tabs

13.1.3 Elemente Generell gilt, Flexform-Elemente sind für das gesamte tca gültig. Daher wollen wir an dieser Stelle nicht ein Abbild des sehr gut dokumentierten tca wiedergeben, sondern nur einen kurzen Überblick über die gängigsten Formularelemente geben. Die Elemente befinden sich innerhalb von <el> des jeweiligen Formulars. Die Bezeichnung des Elements selbst ist beliebig, lediglich die Struktur innerhalb des Elements ist vorgegeben:

252

13.1 Eigene Flexforms Listing 13.6 Struktur eines Flexform-Elements <element1>

In wird festgelegt, um welches Element es sich handelt und welche Eigenschaften/Inhalte es hat. In


Hier können Sie auch gut den Aufbau der Links erkennen. Diese werden zunächst ganz normal erzeugt (hier handelt es sich um eine Seite mit der ID = 1), und als GET-Parameter wird ein Array mit dem Namensraum tx_blogexample_pi1 verwendet. Dieses Array hat als Schlüssel sowohl die action, den controller und letztlich den schon aus TYPO3 4.x bekannten cHash. Wären hier bereits ein oder mehrere Blogs angelegt, würde man zusätzlich die Liste der Blogs bekommen. Sie können dafür ganz einfach auf den Link Create example data klicken – schon sehen Sie diese Liste. Darin wird ein einzelner Blog dann über den Parameter tx_blogexample_pi1[blog][uid]=… angesprochen.

15.7.7

Rückgabe der Ausgabe an den Dispatcher (Schritt 7)

Vor der Rückgabe der Ausgabe wird noch geprüft, ob eventuell zusätzliche Header-Daten ausgegeben werden müssen, und wenn dies geschehen ist, wird über die Methode getContent() des Response-Objekts zurückgegeben.

15.7.8

Rückgabe der Ausgabe an TYPO3 (Schritt 8)

Im letzten Schritt schließlich wird die Ausgabe dann wieder an die Kontrolle an TYPO3 zurückgegeben und dort ggf. mittels TypoScript nachbearbeitet. Im Normalfall schließlich wird der HTML-Inhalt des Plug-ins einfach ausgegeben.

314

16 16 Entwicklung eines eigenen Plug-ins Nachdem wir uns nun in den vorherigen Kapiteln um die Analyse und den Ablauf gekümmert haben, ist es nun an der Zeit, ein eigenes Plug-in zu entwickeln. Wir werden dafür letztlich einen Teil des Blog-Examples nachprogrammieren, weil wir glauben, dass es aus zweierlei Gründen sinnvoll ist. Einerseits haben Sie bereits ein funktionstüchtiges Beispiel, bei dem Sie jederzeit abschauen können, und andererseits können Sie gut zwischen der klassischen Art, Extensions zu schreiben, und der neuen Art mittels Extbase und Fluid vergleichen, da der erste Teil des Buches eben jenes Blog-Example mit der klassischen Programmierung behandelt. Wir werden natürlich nicht das ganze Beispiel komplett nachprogrammieren, aber selbstverständlich alle relevanten und wichtigen Teile, sodass Sie stets wissen, was wo wie und warum gemacht werden muss, um ein bestimmtes Ergebnis zu erhalten.

16.1 Aufbau einer minimal funktionstüchtigen Extension Zurzeit wird ein erweiterter Kickstarter programmiert, mit dem es möglich sein soll, Extensions ebenso wie bereits heute üblich, schnell und einfach, rudimentär zu erstellen, um dann mit diesem Grundgerüst weiter zu arbeiten. Zur Zeitpunkt der Drucklegung war dieser Kickstarter allerdings noch nicht verfügbar. Selbst wenn er mittlerweile erhältlich sein sollte, kann es nicht schaden, zumindest einmal eine Extbase-Extension händisch erstellt zu haben. Somit wissen Sie genau, welche Schritte dazu notwendig sind, und können im Fehlerfall auch schneller und gezielter eingreifen.

16.1.1

Extension-Key

Auch unter Extbase gelten hinsichtlich des Extension-Keys dieselben Grundregeln wie bei der klassischen Extension-Entwicklung. Legen Sie daher einen Namen fest, der mit einem

315

16 Entwicklung eines eigenen Plug-ins kleinen Buchstaben beginnt und lediglich weitere kleine Buchstaben (von a–z) sowie Zahlen und den Unterstrich enthält. Selbst auf den Unterstrich sollten Sie der Einfachheit halber verzichten, obwohl dieser natürlich prinzipiell erlaubt ist. Allerdings wird dieser später intern ohnehin an vielen Stellen getilgt, sodass dies unter Umständen zu Verwirrungen führen kann. Wir wählen daher als Extension-Key den Namen simpleblog.

16.1.2

Verzeichnisse anlegen

Wir fangen damit an, dass wir zunächst leere Verzeichnisse anlegen, welche die Struktur beinhalten. Diese werden wir Stück für Stück mit unseren Dateien füllen. Legen Sie nun als Erstes unterhalb des Verzeichnisses typo3conf/ext/ das Verzeichnis simpleblog an und darin die folgenden Verzeichnisse (genau auf die Schreibweisen achten!):

Abbildung 16.1 Anzulegende Verzeichnisstruktur (grauer Bereich)

16.1.3

Die Datei ext_emconf.php

Anschließend benötigen wir die Datei ext_emconf.php im Root-Verzeichnis unserer Extension (also typo3conf/ext/simpleblog/ext_emconf.php). Diese Datei wird für den Extension-Manager benötigt und dient als Konfigurationsdatei. Fehlt die Datei, kann die

316

16.1 Aufbau einer minimal funktionstüchtigen Extension Extension nicht installiert werden. Insofern kopieren wir uns die Datei schlicht aus der Extension blog_example und passen sie an: Listing 16.1 Die Datei ext_emconf.php 'SimpleBlog', 'description' => 'SimpleBlog-Übungsbeispiel für das Extensionbuch', 'category' => 'plugin', 'author' => 'Patrick Lobacher', 'author_company' => 'typofaktum', 'author_email' => '[email protected]', 'shy' => '', 'dependencies' => 'extbase,fluid', 'conflicts' => '', 'priority' => '', 'module' => '', 'state' => 'alpha', 'internal' => '', 'uploadfolder' => 0, 'createDirs' => '', 'modify_tables' => '', 'clearCacheOnLoad' => 1, 'lockType' => '', 'version' => '0.1.0', 'constraints' => array( 'depends' => array( 'php' => '5.2.0-0.0.0', 'typo3' => '4.3.dev-4.3.99', 'extbase' => '0.0.0-0.0.0', 'fluid' => '0.0.0-0.0.0', ), 'conflicts' => array( ), 'suggests' => array( ), ), '_md5_values_when_last_written' => '', 'suggests' => array( ), ); ?>

16.1.4

Die Dateien ext_localconf.php und ext_tables.php

Diese zwei Dateien sind ebenfalls essenziell für den Ablauf einer Extbase-Extension – diese resultieren aus dem generellen Handling von Extensions nach der herkömmlichen Methode. So wird in die Datei ext_tables.php (die sich ebenfalls im Extension-Root befindet) das Plug-in hinzugefügt:

317

16 Entwicklung eines eigenen Plug-ins Listing 16.2 Die Datei ext_tables.php

Und in der Datei ext_localconf.php wird der Dispatcher konfiguriert. Listing 16.3 Die Datei ext_localconf.php 'index') ); ?>

Vielleicht wundern Sie sich, dass hier zwei der alten Standarddateien benötigt werden, um Extbase zu konfigurieren. Dies hat historische Gründe und liegt darin begründet, dass die Datei ext_tables.php im Backend ausgewertet wird (und somit das Plug-in im Backend zur Verfügung stellt – beispielsweise beim Einfügen auf einer Seite als Seitenelement), und die Datei ext_localconf.php wird im Frontend ausgewertet.

16.1.5

Einrichten eines Standard-Controllers

Damit der Dispatcher überhaupt etwas ausführen kann, benötigen wir natürlich noch einen Controller. Diesen legen wir mit der Datei BlogController.php im Verzeichnis typo3conf/ext/simpleblog/Classes/Controller an: Listing 16.4 Die Datei BlogController.php

Der Controller hat lediglich eine Action (index), die per Default aufgerufen wird. In der Action geben wir nun einen Text direkt aus, was wir später selbstverständlich nicht machen werden, da wir dann gemäß dem MVC-Pattern eine ordentliche View verwenden.

318

16.1 Aufbau einer minimal funktionstüchtigen Extension Aber für ein rudimentär funktionierendes Beispiel soll dies an dieser Stelle erst einmal genügen.

16.1.6

Installieren der Extension und das erste Erfolgserlebnis

Nun sind wir so weit, dass wir die Extension installieren können. Gehen Sie hierzu in das TYPO3 Backend, und wählen Sie das Modul Extension Manager aus. Dort wählen Sie im Pull-down-Menü den Punkt Install Extension, und suchen Sie in der Liste darunter nach dem SimpleBlog-Eintrag. Durch Klick auf das Pluszeichen wird die Extension installiert.

Abbildung 16.2 Die SimpleBlog-Extension im Extension-Manager

Wenn Sie Ihr TYPO3 bereits so eingerichtet haben, dass es bereits Inhalte anzeigen kann, müssen Sie die Extension nur noch als Content-Element auf einer Seite in der Spalte NORMAL einfügen und die Seite im Frontend aufrufen. Und – voilà – wir erhalten die Ausgabe Hallo Welt! Hier ist das SimpleBlog-Beispiel! Wir haben also eine minimal funktionstüchtige Extbase-Extension geschaffen. Diese werden wir nun Stück für Stück erweitern. Ausgabe einrichten für neu installierte TYPO3-Instanzen Wenn Sie Ihr TYPO3 ganz frisch installiert haben, fehlt vielleicht noch die Ausgabe. Obwohl die meisten Leser wahrscheinlich wissen, wie sie in diesem Fall eine Ausgabe konfigurieren, wollen wir dies dennoch ganz kurz erwähnen – schlicht, damit alle Leser auf demselben Stand sind und zudem auch alle dieselbe Ausgabe erhalten, mit der wir später weiter arbeiten werden. Legen Sie also eine neue Seite im Seitenbaum an. Wählen Sie nun links das Modul Template aus und klicken auf die neu angelegte Seite im Seitenbaum. Nun finden Sie auf der rechten Seite einen Button mit der Aufschrift Create template for a new site, den Sie als Nächstes anklicken. Nun wählen Sie den Link mit der Aufschrift Click here to go, den Sie weiter unten auf der Seite finden. Über den Link Click here to edit whole template record gelangen Sie nun zur Eingabe der Template-Parameter. In das nun erscheinende Formular geben Sie im Abschnitt Setup den folgenden TypoScript-Code ein: page = PAGE page.10 < styles.content.get

Nun wählen Sie den Karteireiter Includes. Dort im Feld Include static (from extensions): müssen Sie nun auf der rechten Seite den Eintrag CSS Styled Content (css_styled_content) anklicken, sodass dieser auf der linken Seite ebenfalls auftaucht. Nun noch das Template

319

16 Entwicklung eines eigenen Plug-ins abspeichern (über das Disketten-Symbol ganz oben) und den Cache leeren. Schon ist die Ausgabe Ihres TYPO3-Systems prinzipiell konfiguriert. Erweitern der minimalen Extension Jetzt, nachdem wir eine funktionstüchtige Extension haben, geht es natürlich darum, diese entsprechend zu erweitern. Denn momentan gibt die Extension lediglich einen statischen Text aus. Dafür hätten wir Extbase natürlich nicht gebraucht. Das wird sich aber gleich ändern.

16.2 Hinzufügen eines Views Wie wir vorher gesehen haben, werden die Views im Verzeichnis typo3conf/ext/ simpleblog/Resources/Private/Templates/ gespeichert. In diesem Verzeichnis wer-

den nun alle Entitäten als Verzeichnisse angelegt, und darin schließlich befinden sich die jeweiligen Template-Dateien. Wir legen als das Verzeichnis Blog an und darin eine Datei index.html. Die Benennung der Datei folgt schlicht der Benennung des Views. Die Standard-View wird ja mit index bezeichnet, und ebenso heißt die Standard-Template-Datei – lediglich mit der Extension html.

Abbildung 16.3 Anlegen der Template-Datei index.html

Die Template-Datei füllen wir dann mit folgendem Inhalt:

320

16.2 Hinzufügen eines Views Listing 16.5 Die Template-Datei index.html

Willkommen im SimpleBlog-Beispiel!

Der Name des Blogs lautet: {blogname}

IndexAction



Zuerst geben wir eine ganz normale HTML-Überschrift aus. Dann wollen wir eine Variable ausgeben, die auf den Namen blogname hört. Anschließend wollen wir einen Link erzeugen, der die Default-Action noch einmal aufruft. Extbase sorgt ja bereits dafür, dass per Konvention die Action index aufgerufen wird – aber so sehen wir, wie Actions prinzipiell angesprochen werden. Haben wir nämlich später weitere Actions, so können wir diese genau wie in dem vorliegenden Beispiel ansprechen. Um nun aber die View anzusprechen, müssen wir in den Controller zurückgehen – in unserem Fall in die Datei BlogController.php. Dort ändern wir die Methode indexAction() wie folgt: Listing 16.6 Die Datei BlogController.php view->assign('blogname', "Blog-Beispiel-Name"); } } ?>

Beachten Sie vor allem die hervorgehobene Zeile, da diese im Vergleich zu vorhin die einzige Änderung darstellt. Da die View automatisch initialisiert wird, steht diese als $this->view im Controller immer zur Verfügung. Eine der in der View vorhandenen Methoden lautet nun assign() und ist dafür zuständig, Variablen und Objekte der View zuzuordnen. Dies machen wir, indem wir der Variablen blogname einen statischen Text zuordnen. Selbstverständlich können wir hier auch dynamische Abfragen durchführen und diese Objekten zuordnen. Etwas später werden wir dies auch ausführlich behandeln. Nun können wir das SimpleBlog-Beispiel im Frontend aufrufen und bekommen die gewünschte Ausgabe:

321

16 Entwicklung eines eigenen Plug-ins

Abbildung 16.4 Ausgabe der View

Wenn wir nun auf den Link klicken, stellen wir fest, dass Extbase selbst dafür gesorgt hat, dass dieser die benötigten Informationen enthält. Hier sehen wir auch, wie der Dispatcher intern mit dem Request umgeht: Listing 16.7 Der erzeugte Link (normalerweise natürlich in einer Zeile) – xx = UID der Seite

http://www.example.com/index.php?id=xx &tx_simpleblog_pi1[action]=index &tx_simpleblog_pi1[controller]=Blog &cHash=702d8fea49

So wird der Controller mit der Variablen tx_simpleblog_pi1[controller] und die Action mit der Variablen tx_simpleblog_pi1[action] spezifiziert.

16.3 Entitäten einführen Nun ist es an der Zeit, sich Gedanken über das Domain-Model zu machen. Da wir auf dem Model des Blog-Examples aufbauen, können wir hier natürlich einiges übernehmen. Damit allerdings unser selbst entworfenes Beispiel übersichtlich bleibt, haben wir es grundsätzlich etwas reduziert und nochmals vereinfacht. Zunächst werden wir die Entität Blog verwenden. Diese speichert prinzipiell alle Daten, die direkt zu einem Blog gehören – also den Titel, eine Beschreibung und einige weitere Metadaten. Später werden wir auch noch Posts hinzufügen, aber für den Anfang soll dies erst einmal genügen. Wie wir früher schon mal gezeigt haben, beinhaltet das Model unsere Businesslogik (also die Geschäftslogik) – dafür ist zunächst die Datei Blog.php im Verzeichnis typo3conf/ext/simpleblog/Classes/Domain/Model zuständig. Diese definiert die Entität Blog samt deren Eigenschaften (für den Anfang nur den Titel) und Methoden. Unsere momentane Geschäftslogik soll so definiert sein, dass diese lediglich den Titel des Blogs festlegen und diesen anschließend wieder auslesen kann. Dafür werden wir sogenannte Setter (zum Setzen) und Getter (zum Auslesen) definieren. Diesen Methoden wohnt auch eine gewisse Magie bei – beispielsweise wird der Getter automatisch aufgerufen, wenn es darum geht, den Titel des Blogs auszulesen. Dafür wird ganz prinzipiell der Eigenschaft get vorangestellt und der erste Buchstabe der Eigenschaft in einen großen Buchstaben umgewandelt. Dann wird versucht, die so benannte Methode aufzurufen. Würde man beispielsweise einen Getter für die Eigenschaft name benennen wollen, so würde dieser getName() heißen.

322

16.3 Entitäten einführen Listing 16.8 Die Model-Datei Blog.php title = $title; } /* * Liest den Titel des Blogs aus (Getter) * * @return string The title of the album */ public function getTitle() { return $this->title; } } ?>

Wir haben nun im ersten Schritt die Entität Blog eingeführt durch die Platzierung der Datei Blog.php im oben genannten Verzeichnis. Diese Datei enthält nun die Klasse Tx_SimpleBlog_Domain_Model_Blog (wie sich aus den Namenskonventionen leicht ableiten lässt) und leitet die Basisklasse Tx_Extbase_DomainObject_AbstractEntity ab. Nachdem wir die Entity-Klasse erstellt haben, müssen wir diese natürlich auch noch ansprechen; dies geschieht im Controller – genauer im Blog-Controller in der Datei BlogContoller.php: Listing 16.9 Die Datei BlogController.php setTitle("Mein erstes Blog"); $this->view->assign('blog', $myblog); } } ?>

323

16 Entwicklung eines eigenen Plug-ins Hier wird also ein neues Blog erstellt (instanziiert) und dann der Titel entsprechend gesetzt. Anschließend übergeben wir das Blog-Objekt an die View zum Rendern. Wir müssen daher die View ebenfalls etwas anpassen: Listing 16.10 Die View-Datei index.html

Willkommen im SimpleBlog-Beispiel!

Der Name des Blogs lautet: {blog.title}

IndexAction



Auf den Titel des Blogs wird hier einfach mit {blog.title} zugegriffen. Intern wird für das Auslesen des Titels der vorhin besprochene Getter getTitle() verwendet. Hätten wir dem Blog noch andere Eigenschaften mitgegeben, hätten wir im Template auch einfach darauf mit der Punktsyntax zugreifen können. Dabei wird zunächst das zugewiesene (mittels assign()) Objekt notiert und dann mittels Punkt entweder die Eigenschaft oder andere Objekte. Dies kann beliebig wiederholt werden. Rufen wir nun unsere Extension im Frontend auf, erhalten wir das gewünschte Ergebnis:

Abbildung 16.5 Ausgabe des zugewiesenen Titels

16.4 Daten persistieren (Datenspeicher) Nun ist das obige manuelle Setzen des Titels zwar recht nett, aber doch natürlich auch sehr realitätsfremd. Niemand käme auf die Idee, einen Blog dadurch zu führen, indem er im Quellcode alle Eigenschaften bei jedem Aufruf neu und vor allem manuell setzt. Genau hier kommt die sogenannte Persistenz ins Spiel. Prinzipiell bedeutet dies, dass die Daten irgendwo gespeichert werden müssen, damit diese beim erneuten Aufruf zur Verfügung stehen. Anschließend werden wir uns darum kümmern, wie die Daten per Formular dorthin gelangen. Aber nun der Reihe nach. Wir brauchen also einen Datenspeicher, der unsere Daten „irgendwohin“ schreibt, wenn dies notwendig sein sollte, und diese von dort auch wieder auslesen kann, wenn der Programmablauf dies verlangt. Letztlich wird hierfür bei TYPO3 4.x auch „nur“ eine normale MySQL-Datenbank verwendet – bei FLOW3 allerdings kann dies alles Mögliche sein –

324

16.4 Daten persistieren (Datenspeicher) angefangen von einer Datenspeicherung im Filesystem bis hin zu verteilten Speichern mittels WebDAV, SOAP oder FTP. Da wir aber lediglich eine MySQL-Datenbank benötigen, können wir diese auch genauso klassisch anlegen, wie wir dies bei der herkömmlichen Extension-Programmierung bereits durchgeführt haben. Es gibt hierfür drei Wege, von denen wir zunächst nur den ersten besprechen werden: 1. Manuelles Anlegen der Tabelle und der TCA-Definitionen 2. Anlegen mit dem herkömmlichen Kickstarter 3. Anlegen mit dem neuen, für Extbase optimierten Kickstarter

16.4.1

Datenbankstruktur – die Datei ext_tables.sql

Tabellen werden innerhalb von Extensions bei TYPO3 4.x meist in der Art angelegt, indem die SQL-Kommandos in der Datei ext_tables.sql im Root-Verzeichnis der Extension (also typo3conf/ext/simpleblog/ext_tables.sql) hinterlegt werden. Beim Installieren der Extension wird diese Datei geparst, und die entsprechenden Datenbankfelder werden automatisch angelegt. Prinzipiell werden wir Blogs, Posts und später auch Tags verwalten, daher benötigen wir einige Tabellen dafür. Genau genommen brauchen wir für Blogs, Posts und Tags je eine Tabelle, und für die MM-Beziehung zwischen Posts und Tags (welche wir später inkludieren) brauchen wir noch eine zusätzliche Tabelle. Das soll für den Anfang reichen und kann natürlich beliebig erweitert werden. Wir hinterlegen also in der Datei ext_tables.sql folgende SQL-Befehle: Listing 16.11 Die Datei ext_tables.sql # # Table structure for table 'tx_simpleblog_domain_model_blog' # CREATE TABLE tx_simpleblog_domain_model_blog ( uid int(11) unsigned DEFAULT '0' NOT NULL auto_increment, pid int(11) DEFAULT '0' NOT NULL, tstamp int(11) unsigned DEFAULT '0' NOT NULL, crdate int(11) unsigned DEFAULT '0' NOT NULL, deleted tinyint(4) unsigned DEFAULT '0' NOT NULL, hidden tinyint(4) unsigned DEFAULT '0' NOT NULL; t3ver_oid int(11) DEFAULT '0' NOT NULL, t3ver_id int(11) DEFAULT '0' NOT NULL, t3ver_wsid int(11) DEFAULT '0' NOT NULL, t3ver_label varchar(30) DEFAULT '' NOT NULL, t3ver_state tinyint(4) DEFAULT '0' NOT NULL,

325

16 Entwicklung eines eigenen Plug-ins t3ver_stage tinyint(4) DEFAULT '0' NOT NULL, t3ver_count int(11) DEFAULT '0' NOT NULL, t3ver_tstamp int(11) DEFAULT '0' NOT NULL, t3_origuid int(11) DEFAULT '0' NOT NULL, sys_language_uid int(11) DEFAULT '0' NOT NULL, l18n_parent int(11) DEFAULT '0' NOT NULL, l18n_diffsource mediumblob NOT NULL,

title varchar(255) DEFAULT '' NOT NULL, posts varchar(255) DEFAULT '' NOT NULL, PRIMARY KEY (uid), KEY parent (pid), ); # # Table structure for table 'tx_simpleblog_domain_model_post' # CREATE TABLE tx_simpleblog_domain_model_post ( uid int(11) unsigned DEFAULT '0' NOT NULL auto_increment, pid int(11) DEFAULT '0' NOT NULL, tstamp int(11) unsigned DEFAULT '0' NOT NULL, crdate int(11) unsigned DEFAULT '0' NOT NULL, deleted tinyint(4) unsigned DEFAULT '0' NOT NULL, hidden tinyint(4) unsigned DEFAULT '0' NOT NULL, t3ver_oid int(11) DEFAULT '0' NOT NULL, t3ver_id int(11) DEFAULT '0' NOT NULL, t3ver_wsid int(11) DEFAULT '0' NOT NULL, t3ver_label varchar(30) DEFAULT '' NOT NULL, t3ver_state tinyint(4) DEFAULT '0' NOT NULL, t3ver_stage tinyint(4) DEFAULT '0' NOT NULL, t3ver_count int(11) DEFAULT '0' NOT NULL, t3ver_tstamp int(11) DEFAULT '0' NOT NULL, t3_origuid int(11) DEFAULT '0' NOT NULL, sys_language_uid int(11) DEFAULT '0' NOT NULL, l18n_parent int(11) DEFAULT '0' NOT NULL, l18n_diffsource mediumblob NOT NULL, title varchar(255) DEFAULT '' NOT NULL, tags int(11) unsigned DEFAULT '0' NOT NULL,

326

16.4 Daten persistieren (Datenspeicher)

blog_uid int(11) DEFAULT '0' NOT NULL, blog_table tinytext NOT NULL, PRIMARY KEY (uid), KEY parent (pid), ); # # Table structure for table 'tx_simpleblog_domain_model_tag # CREATE TABLE tx_simpleblog_domain_model_tag ( uid int(11) unsigned DEFAULT '0' NOT NULL auto_increment, pid int(11) DEFAULT '0' NOT NULL, tstamp int(11) unsigned DEFAULT '0' NOT NULL, crdate int(11) unsigned DEFAULT '0' NOT NULL, deleted tinyint(4) unsigned DEFAULT '0' NOT NULL, hidden tinyint(4) unsigned DEFAULT '0' NOT NULL, sys_language_uid int(11) DEFAULT '0' NOT NULL, l18n_parent int(11) DEFAULT '0' NOT NULL, l18n_diffsource mediumblob NOT NULL name varchar(255) DEFAULT '' NOT NULL, posts varchar(255) DEFAULT '' NOT NULL, PRIMARY KEY (uid), KEY parent (pid), ); # # Table structure for table 'tx_simpleblog_post_tag_mm' # CREATE TABLE tx_simpleblog_post_tag_mm ( uid_local int(11) unsigned DEFAULT '0' NOT NULL, uid_foreign int(11) unsigned DEFAULT '0' NOT NULL, tablenames varchar(255) DEFAULT '' NOT NULL, sorting int(11) unsigned DEFAULT '0' NOT NULL, sorting_foreign int(11) unsigned DEFAULT '0' NOT NULL, KEY uid_local (uid_local), KEY uid_foreign (uid_foreign) );

327

16 Entwicklung eines eigenen Plug-ins Wie hier gut zu sehen ist, enthalten die Tabellen sehr viele Verwaltungsinformationen (beispielsweise die t3ver*-Felder, die für die Versionierung der Datensätze zuständig sind, und Ähnliches. Wirklich wichtig sind nur die fett gedruckten Zeilen. Wenn Sie nur diese eingeben, wird unser Beispiel auch wunderbar funktionieren, Allerdings kann es durchaus Sinn machen, die Datensätze einer Versionierung auszusetzen und die Mehrsprachigkeit sowie einige der Backend-Verwaltungsfelder (wie hidden) zu ermöglichen. Um die SQL-Definitionen auszuführen, müssen wir unsere Extension im ExtensionManager im Abschnitt Install Extensions kurz deinstallieren und dann anschließend gleich wieder installieren. Dabei fragt uns TYPO3, ob es die definierten Tabellen anlegen soll, was wir wiederum bejahen.

Abbildung 16.6 Anlegen der Tabellen durch TYPO3

328

16.4 Daten persistieren (Datenspeicher)

16.4.2

Datenbankstruktur – das TCA

Zusätzlich müssen wir aber noch die TCA-Definition anlegen, denn auf diese verlässt sich nicht nur TYPO3 selbst, sondern auch Extbase, wenn es darum geht, wie die Datensätze durch die Persistenzschicht verwaltet werden. In der Datei ext_tables.php legen wir daher die TCA-Einträge an – allerdings lediglich den Unterschlüssel ctrl – alle anderen Daten (insbesondere die Spaltendefinitionen) werden in der Datei typo3conf/ext/simpleblog/Configuration/TCA/TCA.php notiert, die noch anzulegen ist Listing 16.12 Ergänzung in der Datei ext_tables.php … t3lib_extMgm::allowTableOnStandardPages('tx_simpleblog_domain_model_blog' ); $TCA['tx_simpleblog_domain_model_blog'] = array ( 'ctrl' => array ( 'title' => 'Blog', 'label' => 'title', 'tstamp' => 'tstamp', 'crdate' => 'crdate', 'versioningWS' => 2, 'versioning_followPages' => true, 'origUid' => 't3_origuid', 'languageField' => 'sys_language_uid', 'transOrigPointerField' => 'l18n_parent', 'transOrigDiffSourceField' => 'l18n_diffsource', 'delete' => 'deleted', 'enablecolumns' => array( 'disabled' => 'hidden' ), 'dynamicConfigFile' => t3lib_extMgm::extPath($_EXTKEY) . 'Configuration/TCA/tca.php', 'iconfile' => t3lib_extMgm::extRelPath($_EXTKEY) . 'Resources/Public/Icons/icon_tx_blogexample_domain_model_blog.gif' ) ); t3lib_extMgm::allowTableOnStandardPages('tx_simpleblog_domain_model_post' ); $TCA['tx_simpleblog_domain_model_post'] = array ( 'ctrl' => array ( 'title' => 'Post', 'label' => 'title', 'label_alt' => 'author', 'label_alt_force' => TRUE, 'tstamp' => 'tstamp', 'crdate' => 'crdate', 'versioningWS' => 2, 'versioning_followPages' => true, 'origUid' => 't3_origuid', 'languageField' => 'sys_language_uid', 'transOrigPointerField' => 'l18n_parent', 'transOrigDiffSourceField' => 'l18n_diffsource', 'prependAtCopy' => 'LLL:EXT:lang/locallang_general.xml:LGL.prependAtCopy', 'copyAfterDuplFields' => 'sys_language_uid', 'useColumnsForDefaultValues' => 'sys_language_uid', 'delete' => 'deleted', 'enablecolumns' => array( 'disabled' => 'hidden' ),

329

16 Entwicklung eines eigenen Plug-ins 'dynamicConfigFile' => t3lib_extMgm::extPath($_EXTKEY) . 'Configuration/TCA/tca.php', 'iconfile' => t3lib_extMgm::extRelPath($_EXTKEY) . 'Resources/Public/Icons/icon_tx_blogexample_domain_model_post.gif' ) ); $TCA['tx_simpleblog_domain_model_tag'] = array ( 'ctrl' => array ( 'title' => 'Tags', 'label' => 'name', 'tstamp' => 'tstamp', 'crdate' => 'crdate', 'delete' => 'deleted', 'enablecolumns' => array ( 'disabled' => 'hidden' ), 'dynamicConfigFile' => t3lib_extMgm::extPath($_EXTKEY) . 'Configuration/TCA/tca.php' ) ); …

Hier werden nun einige Dinge erledigt:

„ Durch die Funktion allowTableOnStandardPages() wird sichergestellt, dass der Datensatz nicht nur auf der absoluten Root-Seite (mit der UID=0) eingefügt werden kann, sondern auf jeder beliebigen Seite.

„ Es werden die relevanten Tabellen mittels TCA-Array definiert. Die MM-Tabelle tx_simpleblog_post_tag_mm fehlt hierbei, weil wir auf diese Tabelle keinen direk-

ten Zugriff benötigen.

„ Über die TCA-Option dynamicConfigFile werden die Spaltendefinitionen des TCA nachgeladen. Für alle Tabellen wird dafür die Datei TCA.php angegeben. Dies könnten aber natürlich auch individuelle Dateien sein. Nun legen wir noch die Datei typo3conf/ext/simpleblog/Configuration/TCA/ TCA.php an und füllen diese mit den jeweiligen Spaltendefinitionen: Listing 16.13 Inhalt der Datei TCA.php
die ('Access denied.');

$TCA['tx_simpleblog_domain_model_blog'] = array( 'ctrl' => $TCA['tx_simpleblog_domain_model_blog']['ctrl'], 'interface' => array( 'showRecordFieldList' => 'hidden, title, description, logo, posts, administrator' ), 'columns' => array( 'title' => array( 'exclude' => 0, 'label' => 'Blog-Titel', 'config' => array( 'type' => 'input', 'size' => 20, 'eval' => 'trim,required', 'max' => 256 ) ),

330

16.4 Daten persistieren (Datenspeicher) 'posts' => array( 'exclude' => 1, 'label' => 'Posts', 'config' => array( 'type' => 'inline', 'loadingStrategy' => 'storage', 'foreign_class' => 'Tx_Simpleblog_Domain_Model_Post', 'foreign_table' => 'tx_simpleblog_domain_model_post', // TODO Re-enable the foreign key references by uncommenting the following lines // 'foreign_field' => 'blog', 'maxitems' => 999999, // TODO This is only necessary because of a bug in tcemain 'appearance' => array( 'newRecordLinkPosition' => 'bottom', 'collapseAll' => 1, 'expandSingle' => 1, ), ) ) ), 'types' => array( '1' => array('showitem' => 'hidden, title, description, logo, posts, administrator') ), 'palettes' => array( '1' => array('showitem' => '') ) ); $TCA['tx_simpleblog_domain_model_post'] = array( 'ctrl' => $TCA['tx_simpleblog_domain_model_post']['ctrl'], 'interface' => array( 'showRecordFieldList' => 'hidden, title, date, author, content, tags, comments, related_posts' ), 'columns' => array( 'title' => array( 'exclude' => 0, 'label' => 'Post-Titel', 'config' => array( 'type' => 'input', 'size' => 20, 'eval' => 'trim, required', 'max' => 256 ) ), 'tags' => array( 'exclude' => 1, 'label' => 'Post-Tags', 'config' => array( 'type' => 'select', 'size' => 10, 'minitems' => 0, 'maxitems' => 9999, 'autoSizeMax' => 30, 'multiple' => 1, 'foreign_class' => 'Tx_Simpleblog_Domain_Model_Tag', 'foreign_table' => 'tx_simpleblog_domain_model_tag', 'MM' => 'tx_simpleblog_post_tag_mm', 'MM_insert_fields' => array('tablenames' => 'tx_simpleblog_domain_model_tag'), 'MM_match_fields' => array('tablenames' => 'tx_simpleblog_domain_model_tag'), ) ), 'blog' => array( // TODO Re-enable the foreign key references by uncommenting the following lines // 'config' => array(

331

16 Entwicklung eines eigenen Plug-ins // // )

'type' => 'passthrough',

), ), 'types' => array( '1' => array('showitem' => 'hidden, title, date, author, content, tags, comments, related_posts') ), 'palettes' => array( '1' => array('showitem' => '') ) ); $TCA['tx_simpleblog_domain_model_tag'] = array( 'ctrl' => $TCA['tx_simpleblog_domain_model_tag']['ctrl'], 'columns' => array( 'name' => array( 'exclude' => 0, 'label' => 'TagName', 'config' => array( 'type' => 'input', 'size' => 20, 'eval' => 'trim, required', 'max' => 256 ) ), 'posts' => array( 'exclude' => 1, 'label' => 'Posts', 'config' => array( 'type' => 'select', 'size' => 10, 'minitems' => 0, 'maxitems' => 9999, 'autoSizeMax' => 30, 'multiple' => 0, 'foreign_class' => 'Tx_simpleblog_Domain_Model_Post', 'foreign_table' => 'tx_simpleblog_domain_model_post', 'MM' => 'tx_simpleblog_post_tag_mm', 'MM_opposite_field' => 'tags', 'MM_insert_fields' => array('tablenames' => 'tx_simpleblog_domain_model_tag'), 'MM_match_fields' => array('tablenames' => 'tx_simpleblog_domain_model_tag'), ) ), ) ); ?>

Mit dieser TCA-Definition wird nun Folgendes erreicht:

„ Blogs werden in der Tabelle tx_simpleblog_domain_model_blog gespeichert. „ Posts werden einerseits in der Tabelle tx_simpleblog_domain_model_post gespeichert und andererseits im Feld posts der Tabelle tx_simpleblog_domain_model_ blog referenziert. Hierbei werden dort die UIDs der Post-Beiträge als kommaseparierte Liste gespeichert. Dies wird über die foreign_*-Optionen des TCA erreicht.

„ Tags werden einerseits in der Tabelle

tx_simpleblog_domain_model_tag gespei-

chert und andererseits im Feld tags der Tabelle tx_simpleblog_domain_model_ posts referenziert. Hier wird allerdings – im Gegensatz zu den Posts im Blog – ledig-

lich die Anzahl der Tags in der Tabelle tx_simpleblog_domain_model_posts im

332

16.5 Anlegen eines Repositorys Feld tags abgespeichert. Die dazugehörigen Referenzen werden in die MM-Tabelle tx_simpleblog_post_tag_mm eingetragen. Das Handling für 1:N oder M:M-Relationen ist also prinzipiell komplett identisch – lediglich das TCA gibt vor, wie Extbase damit umgehen soll.

16.5 Anlegen eines Repositorys Vorhin haben wir ein Blog derart erzeugt, indem wir schlicht die Klasse instanziiert haben. Dies ist natürlich nicht praktikabel, da wir das so erzeugte Objekt schlecht speichern können und auch Extbase von dessen Existenz schlicht nichts weiß. Um nun ein Objekt vorschriftsmäßig zu erzeugen, werden wir gemäß der Paradigmen des Domain Driven Designs ein sogenanntes Repository verwenden. Dieses stellt die Schnittstelle von Domain und Infrastruktur dar und wird im Verzeichnis typo3conf/ext/simpleblog/Classes/Domain/Repository mit dem Namen EntityRepository.php erwartet. Hat man also ein Repository für die Entität Blog, so lautet die zugehörige Datei BlogRepository.php, und genau diese legen wir nun an. Listing 16.14 Die Datei BlogRepository.php

Nanu, werden Sie sich jetzt fragen – die Klasse ist ja komplett leer. Das hat tatsächlich seine Richtigkeit. Dadurch, dass die Klasse Tx_Extbase_Persistence_Repository abgeleitet wird, enthält das Repository bereits alle für den Anfang notwendigen Funktionalitäten. So kann beispielsweise ein Objekt (in unserem Fall ein Blog) zum Repository mittels add() hinzugefügt oder mittels remove() entfernt werden. Darüber hinaus stehen bereits per Default Methoden zum Finden alle Objekte (findAll()) oder über die magische Funktion findByEigenschaft() eine Funktion zur Verfügung, die nur diejenigen Objekte findet, deren Eigenschaft einen bestimmten Wert aufweist. So lässt sich beispielsweise nach dem Titel eines Objekts suchen, indem man findByTitle() verwendet.

Anpassen des Blog-Controllers Damit unser Repository auch benutzt wird, müssen wir es im Blog-Controller direkt ansprechen:

333

16 Entwicklung eines eigenen Plug-ins Listing 16.15 Änderung an der Datei BlogController.php blogRepository = new Tx_Simpleblog_Domain_Repository_BlogRepository; } public function indexAction() { $blogs = array(); for ($i=0; $i<3; $i++) { $myblog = new Tx_Simpleblog_Domain_Model_Blog; $myblog->setTitle('Das ist mein '.$i.'. Blog'); $this->blogRepository->add($myblog); } $this->view->assign('blogs', $this->blogRepository->findAll()); } } ?>

Wir haben den Controller nun um eine zusätzliche „magische“ Methode erweitert, und zwar initializeAction(). Diese Methode wird immer dann aufgerufen, wenn eine Action angefordert wird. Dabei wird kurz zuvor diese Methode angesprungen, und die dort enthaltenen Anweisungen werden noch kurz vor der eigentlichen Action ausgeführt. In diesem Fall wird das Blog-Repository initialisiert. Auf dieses kann später mittels $this>repository zugegriffen werden. Anschließend wird nun auch die Index-Action entsprechend angepasst, indem beispielhaft eine Schleife drei Blogs erzeugt. Der Titel der Blogs wird entsprechend gesetzt und mittels add() im Repository gespeichert. Schließlich werden über die dem Repository eigene Funktion findAll() alle Blogs gesucht und dem View zugeordnet. Zusätzlich müssen wir hier also auch den View entsprechend anpassen: Listing 16.16 Änderungen an der View-Datei index.html

Blog hinzufügen



Hier wird ein neuer View-Handler verwendet, der ähnlich einer foreach-Schleife alle vorher angelegten und persistierten (gespeicherten) Blogs durchgeht und deren Titel in einer ungeordneten Liste zurückgibt. Das Ergebnis nach zweimaligem Neuladen der Seite kann sich durchaus sehen lassen:

334

16.6 Anlegen einer neuen Action

Abbildung 16.7 Ausgabe nach zweimaligem Reload der Seite

Allerdings werden nun bei jedem Reload drei neue Blogs erzeugt. Das ist unter Umständen nicht logisch, sodass es durchaus Sinn macht, das Hinzufügen in eine eigene Action auszulagern und die Index-Action lediglich zum Anschauen der bereits hinzugefügten Blogs zu nutzen. In der Anzeige haben wir ja bereits einen Link angelegt, der mit der Action add versehen wurde – diese werden wir nun mit Leben füllen. Denn bislang ist diese Action nicht definiert, daher springt der Dispatcher immer wieder in die Default-Action index.

16.6 Anlegen einer neuen Action Für eine neue Action brauchen wir zunächst einmal einen neuen Eintrag in der Datei ext_localconf.php. Dort verändern wir den Aufruf der Funktion configureDispatcher(): Listing 16.17 Änderung in der Datei ext_localconf.php Tx_Extbase_Utility_Plugin::configureDispatcher( $_EXTKEY, 'Pi1', array('Blog' => 'index,add'), array('Blog' => 'add') );

Da das Zufügen eines Blogs sicherlich nicht gechachet werden soll, wurde die Action zusätzlich noch einmal in der zweiten Zeile (hervorgehoben) zugefügt. Diese wird dann als USER_INT-Objekt im TypoScript-Code eingefügt. Aussagekräftige Fehlermeldungen Wenn Sie nun einmal probieren, die Website in diesem Stadium aufzurufen, bekommen Sie eine – wie wir finden – sehr anschauliche und aussagekräftige Fehlermeldung.

335

16 Entwicklung eines eigenen Plug-ins

Abbildung 16.8 Ausführliche Fehlermeldung mit Extbase

Dabei wird zunächst der Fehler angegeben – so fehlt dem Programm wohl eine Action mit dem Namen addAction(). Und etwas später sehen Sie dann auch, in welcher Klasse die Action fehlt, nämlich in Tx_Simpleblog_Controller_BlogController. So müssen wir, nach Lektüre dieser Meldung, die Action dort in der entsprechenden Datei einfach nur einfügen. Wichtig ist, dass Sie die Meldungen aufmerksamen studieren und interpretieren können – dies fällt aber aufgrund der Ausführlichkeit ziemlich leicht, sodass Ihnen Extbase eigentlich immer genau sagt, was es benötigt, um den Fehler zu beheben.

16.6.1

Anlegen der Action addAction()

Nun führen wir in der Controller-Klassendatei typo3conf/ext/simpleblog/Classes/ Controller/BlogController.php folgende Veränderung durch: Listing 16.18 Die geänderte Datei BlogController.php public function indexAction() { $this->view->assign('blogs', $this->blogRepository->findAll()); } public function addAction() { for ($i=0; $i<3; $i++) { $myblog = new Tx_Simpleblog_Domain_Model_Blog; $myblog->setTitle('Das ist mein '.$i.'. Blog'); $this->blogRepository->add($myblog); } $this->redirect('index'); }

Wir haben nun in der Index-Action lediglich die Anzeige der Blogs untergebracht und in der neuen Add-Action das Anlegen der Blogs. Nach dem Anlegen schließlich wird mit dem API-Befehl redirect() wieder auf die Index-Action umgeleitet. Somit wird damit wieder die Liste der Blogs angezeigt. Das Template bleibt dabei unverändert.

336

16.7 Daten per Formular eingeben und auswerten

16.7 Daten per Formular eingeben und auswerten Störend ist natürlich jetzt noch, dass der Titel ausschließlich fest vorgegeben wird und nicht etwa vom User über ein Formular eingegeben werden kann. Da dies natürlich dringend notwendig ist, rüsten wir diese Funktionalität nun nach. Dafür müssen wir zunächst ein neues Template mit dem Namen add.html erstellen, das wir ebenfalls in das Verzeichnis typo3conf/ext/simpleblog/Resources/Private/ Templates/Blog legen. Listing 16.19 Das neue Template add.html

Neues Blog anlegen



Blog anlegen


Über einen neuen View-Handler können wir nun auch Formulare erzeugen lassen. Der Handler enthält auch gleich die notwendigen Parameter, damit der Dispatcher weiß, was er mit den Daten machen soll. So werden die Formulardaten durch den Controller Blog und die (für uns neue) Action create verarbeitet. Somit müssen wir zunächst diese Action in der Datei ext_localconf.php zufügen: Listing 16.20 Änderungen in der Date ext_localconf.php … Tx_Extbase_Utility_Plugin::configureDispatcher( $_EXTKEY, 'Pi1', array('Blog' => 'index,add,create'), array('Blog' => 'add,create') ); …

Im Blog-Controller nun müssen wir diese Action natürlich einfügen und die Add-Action entsprechend anpassen. Denn unser Plan ist, dass wir zunächst über den Link „Blog hinzufügen“ die Action add aufrufen, die das Formular enthält, in das wir den Namen des Blogs eingeben können. Beim Abschicken des Formulars wird die Action create aufgerufen, die das Blog dann schließlich anlegt. Sobald das Blog dann in das Repository gespeichert wurde, soll eine Weiterleitung auf die Action index erfolgen, die alle angelegten Blogs anzeigt. Nun müssen wir also noch die beiden Actions entsprechend einrichten:

337

16 Entwicklung eines eigenen Plug-ins Listing 16.21 Änderungen in der Datei BlogController.php public function addAction(Tx_Simpleblog_Domain_Model_Blog $newBlog = NULL) { $this->view->assign('newBlog', $newBlog); } public function createAction(Tx_Simpleblog_Domain_Model_Blog $newBlog) { $this->blogRepository->add($newBlog); $this->redirect('index'); }

Interessant sind in beiden Fällen die Parameter der jeweiligen Methoden. Bei der Methode addAction() wird ein leeres Blog übergeben, das allerdings vom Typ der Klasse Tx_Simpleblog_Domain_Model_Blog ist. Damit weiß Extbase automatisch, welche Eigenschaften verwaltet werden müssen. Die Create-Action schließlich bekommt das Objekt vom Formular zurück – allerdings mit gefülltem Titel – und fügt es zum Repository hinzu.

16.8 Objekte aus dem Repository entfernen Mit der Methode add() haben wir bislang Objekte dem Repository zufügen können, nun wollen wir diese natürlich auch wieder entfernen können. Dafür platzieren wir bei der Auflistung aller Blogs am Ende einen Link Delete, mit dem wir das spezielle Blog auch wieder entfernen können. Dies geschieht mit einer eigenen Action delete.

16.8.1

Zufügen der Action delete in der Konfiguration

In der Datei ext_localconf.php müssen wir nun also zunächst wieder unsere neue Action zufügen: Listing 16.22 Änderungen in der Datei ext_localconf.php Tx_Extbase_Utility_Plugin::configureDispatcher( $_EXTKEY, 'Pi1', array('Blog' => 'index,add,create,delete'), array('Blog' => 'add,create,delete') );

16.8.2

Anpassen der Template-Datei index.html

Weiter geht es mit der Template-Datei index.html, indem wir den Lösch-Link platzieren müssen: Listing 16.23 Änderungen in der Template-Datei index.html:
  • {blog.title} (Delete)


  • 338

    16.9 Update von Objekten Als Argument übergeben wir der Action delete schließlich das aktuelle Blog-Objekt (hintere blog-Bezeichnung) in der gleichnamigen Variablen blog (vordere blog-Bezeichnung). Extbase weiß automatisch, welches exakte Blog-Objekt gemeint ist, und extrahiert selbstständig die jeweilige ID heraus. 16.8.2.1

    Zufügen der Action delete im Blog-Controller

    Nun müssen wir noch den Blog-Controller anpassen, indem wir die Action delete zufügen, und schon haben wir (nach dem Löschen des Caches) unser Ziel erreicht: Listing 16.24 Zufügen der Action delete in der Datei BlogController.php public function deleteAction(Tx_Simpleblog_Domain_Model_Blog $blog) { $this->blogRepository->remove($blog); $this->redirect('index'); }

    Über die API-Methode remove() wird nun das übergebene Blog aus dem Repository gelöscht. Nach dem Löschen wird wie gewohnt zur Action index weitergeleitet. Weitere API-Methoden im Repository lauten replace() und update() – diese werden im Referenz-Teil des Buches näher erläutert.

    16.9 Update von Objekten Während wir nun sowohl Objekte angelegt wie auch gelöscht haben, wollen wir uns nun den dritten möglichen Fall der schreibenden Interaktion mit dem Repository ansehen – das Aktualisieren bzw. Update.

    16.9.1

    Edit- und Update-Action hinzufügen

    Um ein Update durchführen zu können, müssen wir einerseits ein Änderungsformular präsentieren und andererseits diese Änderung dann auch im Repository durchführen. Dafür benötigen wir zwei Actions – einmal eine Edit-Action, die das Formular präsentiert, und dann die Update-Action, die die Änderung in das Repository schreibt. Wir müssen also nun die beiden Actions edit und update erst einmal in der ext_localconf.php registrieren und andererseits diese Action im Blog-Controller ausformulieren. Listing 16.25 Änderung in der Datei ext_localconf.php Tx_Extbase_Utility_Plugin::configureDispatcher( $_EXTKEY, 'Pi1', array( 'Blog' => 'index,add,create,edit,update,delete', ), array(

    339

    16 Entwicklung eines eigenen Plug-ins 'Blog' => 'add,create,edit,update,delete', ) );

    Im Blog-Controller müssen wir zudem die beiden Actions ausformulieren: Listing 16.26 Änderungen in der Datei BlogController.php /** * Editiert einen bestehenden Blog * * @param Tx_Simpleblog_Domain_Model_Blog $blog Der ursprüngliche Blog * @return string Formular, um den Blog zu bearbeiten * @dontvalidate $blog */ public function editAction(Tx_Simpleblog_Domain_Model_Blog $blog) { $this->view->assign('blog', $blog); } /** * Aktualisiert einen bestehenden Blog * * @param Tx_Simpleblog_Domain_Model_Blog $blog Eine noch nicht gespeicherte, aber bereits modifierte Kopie des ursprünlichen Blogs * @return void */ public function updateAction(Tx_Simpleblog_Domain_Model_Blog $blog) { $this->blogRepository->update($blog); $this->redirect('index'); }

    Nun brauchen wir einerseits einen Link zum Editieren des Blogs in der Template-Datei der Index-Action: Listing 16.27 Änderung in der Datei index.html
  • {blog.title} ( Edit Blog / Delete) )


  • Und andererseits ein eigenes Template mit dem Dateinamen edit.html im Verzeichnis: typo3conf/ext/simpleblog/Resources/Private/Templates/Blog/: Listing 16.28 Die Datei edit.html

    Edit Blog "{blog.title}"

    Bitte geben Sie hier Ihre Änderungen ein:



    Edit


    340

    16.10 Der Query-Manager Wenn Sie nun das Beispiel im Frontend aufrufen, bekommen Sie hinter jedem Blog einen Link Edit Blog angezeigt, über den Sie ein Formular zum Ändern erhalten. Dort sind automatisch bereits die Eigenschaften in den Feldern vorbelegt – diese werden von Fluid an dieser Stelle automatisch aus dem übergebenen Blog-Objekt extrahiert. Nach der Änderung klicken Sie einfach auf den Button Edit, und schon wurde die Änderung auch im Blog-Repository persistiert.

    16.10 Der Query-Manager Wir haben im Repository bislang nur die Methode findAll() verwendet, um alle gespeicherten Blogs zu erhalten. Wer bereits einige Blogs angelegt hat, dem fällt zunächst auf, dass diese in genau der Reihenfolge wieder ausgelesen wurden, in der sie hinzugefügt wurden. Dies und eben die Auswahl der Blogs selbst wollen wir nun mithilfe des Query-Managers anpassen. Dafür stehen uns weitere „magische“ Methoden des Repositorys zur Verfügung,

    „

    findByUid($uid)

    Findet ein Objekt anhand einer übergebenen UID (die man natürlich kennen muss)

    „

    findBy[Eigenschaft]($search)

    Findet alle Objekte, deren Eigenschaft den Wert $search haben. Dabei wir die Eigenschaft selbst Teil des Funktionsnamens. So lautet die Suche nach dem Titel findByTitle($search), wenn die Eigenschaft $title heißt.

    „

    findOneBy[Eigenschaft]($search)

    Findet das erste auftretende Objekt, dessen Eigenschaft den Wert $search hat. Dabei wird die Eigenschaft selbst Teil des Funktionsnamens – exakt wie bei der vorherigen Funktion. Diese drei genannten Funktionen stehen in jedem Repository automatisch zur Verfügung. Was ist aber, wenn man eigene, individuelle Abfragen braucht? Dann kommt der QueryManager ins Spiel, der im sogenannten Persistence-Framework zu Hause ist – genauer in der Datei typo3conf/ext/extbase/Classes/Persistence/QueryFactory.php. Die Query-Factory schließlich implementiert das Interface Tx_Extbase_Persistence_ QueryInterface, und dieses enthält nun genau die Methoden, mit denen wir unsere Suchabfragen im Repository anpassen können. Um nun einen eigene Query abzusetzen, müssen wir das (bislang noch völlig leere) BlogRepository mit einer geeigneten Methode erweitern:

    341

    16 Entwicklung eines eigenen Plug-ins Listing 16.29 Eigene Query-Methode für die Datei BlogRepository.php public function findSpecial() { $query = $this->createQuery(); … $blogs = $query->execute(); return $blogs; }

    Wir erzeugen in der Funktion findSpecial() zunächst ein Query-Objekt. Mit diesem werden wir gleich interessante Dinge anstellen, daher haben wir diesen Teil erst einmal mit drei Punkten angedeutet. Anschließend wird der Query ausgeführt, wobei alle gefundenen Objekte in der Variablen $blogs gespeichert werden, die dann auch zurückgegeben wird. In der Index-Action des BlogControllers müssen wir jetzt natürlich auch den Query-Aufruf entsprechend anpassen: Listing 16.30 Änderung in der Index_Action der Datei BlogController.php public function indexAction() { //$this->view->assign('blogs', $this->blogRepository->findAll()); $this->view->assign('blogs', $this->blogRepository->findSpecial()); }

    Wir haben den ursprünglichen Aufruf auskommentiert und den Aufruf mit der neuen Methode zugefügt. Ruft man das Frontend nun auf, bekommt man genau dieselbe Anzeige wie vorher – das liegt primär daran, dass wir den Query noch nicht näher spezifiziert haben und somit das Default-Verhalten (finde alle Objekte) automatisch aktiviert wird. Nun fügen wir zur Query-Methode eine Möglichkeit hinzu, alle Blogs zu finden, in deren Titel der String „ist mein“ vorkommt (da wir vorhin Blogs mit dem Titel „Das ist mein x.Blog!“ angelegt hatten). Listing 16.31 Die überarbeitete Methode findSpecial() public function findSpecial() { $query = $this->createQuery(); $query->matching( $query->like('title', '%ist mein%') ); $blogs = $query->execute(); return $blogs; }

    Über die Funktion matching() wird grundsätzlich eine Suche eingeleitet. In diesem Fall bemühen wir nun die Funktion like(), die ähnlich wie bei SQL Wortbestandteile finden kann. Da wir tatsächlich im Hintergrund eine MySQL-Datenbank verwenden, wird somit ein Query der Art: SELECT * FROM tx_simpleblog_domain_model_blog WHERE title LIKE '%ist mein%' abgesetzt, und die Ergebnisse der Abfrage werden als Objekt verpackt zurückgesendet. Der Query-Manager bietet hier noch zahlreiche andere Methoden, die einerseits beliebig kombiniert und andererseits in beliebiger Reihenfolge aufgerufen werden können.

    342

    16.11 Eingabevalidierung So gibt es setLimit(), setOrderings und setOffset(), die auf das ganze Ergebnis angewendet werden können, und logicalAnd(), logicalOr(), logicalNot(), equals(), lessThan(), lessThanOrEqual(), greaterThan() und greaterThanOrEqual() für die Teilergebnisse. Damit lassen sich praktisch alle wichtigen Abfragen datenherkunftsneutral beschreiben. Wenn Sie damit nicht zum erwünschten Ergebnis kommen, gibt es zusätzlich die Möglichkeit, einen SQL-Query (sofern eine Datenbank als Speichermedium verwendet wurde) abzusetzen: $query->statement(SELECT * FROM tx_simpleblog_domain_model_blog WHERE title LIKE '%ist mein%');

    16.11 Eingabevalidierung Hin und wieder ist es hilfreich und auch nötig, eigene GET- oder POST-Variablen zu übermitteln und diese auszuwerten. Hier wurde mit Extbase ein leicht anderes Konzept eingeführt. So müssen alle Eingabevariablen zunächst sowohl registriert wie auch spezifiziert werden. Dies geschieht über die Annotation. Wollen wir beispielsweise in der Index-Action die GET-Variable $name auswerten, so geht das erst einmal nur im Namensraum der er Extension – sprich in unserem Fall würde die Variable tx_simpleblg_pi1[name] lauten. In der Extension selbst wird diese Variable aber auch immer mit $name angesprochen. Als Nächstes müssen wir die Variable als Input-Parameter an die Index-Action übergeben und eben in der zugehörigen Annotation die Variable registrieren und spezifizieren.

    16.12 Validatoren Extbase enthält ein umfangreiches Validation-Framework, das ebenfalls von FLOW3 rückportiert wurde. Damit ist es möglich, eigene Validatoren sowie auch vordefinierte Validatoren verwenden. So kann man damit einfache Fälle, wie die Überprüfung auf Datentypen, Längen und Ähnliches, schnell lösen. Sobald man dann eigene Überprüfungen benötigt, kann man einen eigenen speziellen Validator einsetzen.

    16.12.1 Vordefinierte Validatoren Um auf das Set an vordefinierten Validatoren zuzugreifen, müssen Sie diese über eine Annotation festlegen. Wir wollen beispielsweise sicherstellen, dass der Name eines Blogs sowohl ein Text sein soll wie auch die Mindestlänge von fünf Zeichen haben muss. Dafür fügen wir folgenden Kommentar in der Datei Blog.php vor der Definition der Eigenschaft $title ein.

    343

    16 Entwicklung eines eigenen Plug-ins Listing 16.32 Kommentar in der Datei Blog.php /** * Der Titel des Blogs * * @var string * @validate Text, StringLength(minimum = 5, maximum = 80) * @identity */ protected $title = '';

    Die wirklich entscheidende Zeile haben wir hervorgehoben. Beachten Sie bitte, dass eine Annotation im Gegensatz zu einem reinen PHP-Kommentar mit der Zeichenkette /** eingeleitet wird. Geben Sie etwa nur /* an, so wird der Kommentar von Extbase überhaupt nicht ausgewertet. Über die Annotation @validate wird die Validierung nun eingeschaltet. Nachfolgend werden die möglichen Validatoren angegeben. Wenn es (wie hier) mehrere Validatoren sind, so werden diese per Komma voneinander getrennt und dann später bei der Validierung sequenziell von links nach rechts durchlaufen. An erster Stelle steht hier Text, was bedeutet, dass die Eigenschaft $title daraufhin geprüft wird, dass diese nur Text (darf keine Tags beinhalten) enthalten darf. Der nächste Validator, der auf die Eigenschaft angewendet wird, ist der StringLength-Validator. Dieser prüft die Länge der Eigenschaft und löst einen Fehler aus, wenn diese kürzer als zwei Zeichen und länger als 80 Zeichen ist. Alle verfügbaren, vordefinierten Validatoren finden Sie übrigens im Verzeichnis typo3conf/ext/extbase/Classes/Validation/Validator/ und natürlich im Referenzteil des Buches. Wenn Sie nun den Cache löschen, die Seite erneut aufrufen, auf den Link Blog einfügen klicken und dann einen Blognamen mit nur einem Buchstaben angeben, wird das Blog nicht eingetragen, sondern das Eingabefeld wird erneut angezeigt – allerdings rot unterlegt.

    Abbildung 16.9 Der Name des Blogs muss fünf Buchstaben oder mehr haben.

    16.12.2 Eigene Validatoren Um einen eigenen Validator in Aktion zu sehen, wollen wir festlegen, dass ein neuer Blog nicht den Namen „Extbase“ haben darf.

    344

    16.12 Validatoren Wir müssen hierfür zunächst eine Datei BlogValidator.php im Verzeichnis typo3conf/ext/simpleblog/Classes/Domain/Validator/ anlegen. Listing 16.33 Die Datei BlogValidator.php getTitle() === 'Extbase') { $this->addError('"Extbase" kann nicht als Blogname verwendet werden', 2); return FALSE; } return TRUE; } } ?>

    Prinzipiell wird bei Vorhandensein eines Validators, dessen Namen sich aus der Entität und dem String Validator sowie der Dateiendung .php (wie BlogValidator.php oder PostValidator.php) bildet, dieser automatisch zur Validierung der Entität verwendet. Die Benennung der Klasse erfolgt dabei der Konvention – beispielsweise Tx_Simpleblog_Domain_Validator_BlogValidator. Diese Klasse erweitert dabei die Klasse Tx_Extbase_Validation_Validator_AbstractValidator. In der Klasse schließlich wird auf das Vorhandensein der Methode isValid($object) geprüft. Dieser wird das zu überprüfende Objekt übergeben, und die Methode liefert schließlich TRUE oder FALSE zurück. Im letzteren Fall ist die Validierung fehlgeschlagen. Wir prüfen im obigen Beispiel schlicht, ob der Titel des anzulegenden Blogs identisch mit Extbase ist, und liefern FALSE zurück, falls dem tatsächlich so sein sollte. Da der Validator automatisch aufgerufen wird, müssen wir diesen nirgendwo explizit angeben. Wollen wir nun aber einen ganz speziellen eigenen Validator verwenden, so müssen wir dies mittels Annotation entsprechend mitteilen. So wollen wir beispielsweise nur Löschungen von Blogs zulassen, dessen Titel eine gewisse Länge hat. Die Längenangaben wollen wir in der Annotation festlegen und vom Validator auslesen.

    345

    16 Entwicklung eines eigenen Plug-ins Listing 16.34 Annotation zur Überprüfung der Länge des Blog-Titels im Blog-Contoller /** * @param Tx_Simpleblog_Domain_Model_Blog $blog Blog to be deleted * @validate $blog Tx_Simpleblog_Domain_Validator_TitleLengthValidator (minimum = 2) * @return void */ public function deleteAction(Tx_Simpleblog_Domain_Model_Blog $blog) { …

    Hier wird (im Gegensatz zur Domain-Klasse) der Validator zunächst zwar auch mit @validate eingeleitet, dann wird aber die zu überprüfende Variable oder das Objekt (hier der Blog) genannt. Nun folgt der Name der Validator-Klasse als „Fully Qualified Class Name“ – sprich als vollständiger Klassenname. Im Verzeichnis typo3conf/ext/simpleblog/Classes/Domain/Validator/ legen wir nun die Datei TitleLengthValidator.php an. Listing 16.35 Die Datei TitleLengthValidator.php errors = array(); $minimum = (isset($this->options['minimum'])) ? $this>options['minimum']: 1; $stringLength = strlen($blog->getTitle()); if ($stringLength > $minimum) { return TRUE; } else { $this->addError('Die Laenge des Titels muss groesser als ' . $this->options['minimum'] . ' sein!', 1238133067); return FALSE; } } } ?>

    Die in der Annoation zum Validator angegebenen Optionen, die in runde Klammern gesetzt und bei mehreren Optionen mit Komma getrennt werden, können in der Validatorklasse mittels $this->options['optionen-name'] ausgelesen werden. Extbase sorgt nämlich automatisch dafür, dass sämtliche Optionen registriert und in eben dieser Variablen zur Verfügung gestellt werden.

    346

    16.12 Validatoren Schließlich wird bei Ungültigkeit ein Fehler mittels der Funktion addError() registriert. Am Ende der Funktion wird zusätzlich eine Fehlernummer angegeben, die prinzipiell frei wählbar ist und zur späteren Identifikation des Fehlers dient.

    16.12.3 Ausgabe der Fehler Nun, da ein Fehler in einem Validator aufgetreten ist, will man diesen natürlich auch sichtbar machen. Dies ist ohne Probleme im Template möglich. Fügen Sie dazu in Ihrem Template einfach folgenden Code hinzu, und schon wird die Fehlermeldung samt Fehlercode ausführlich ausgegeben: Listing 16.36 Ausgabe von Fehlern im Template
    {error.message}

    <strong>{error.propertyName}: {error.code}: {errorDetail.message}



    16.12.4 Optionale Argumente Oft gibt es den Fall, dass man zwar einen Validator bestimmt, der ein Argument überprüfen soll, aber man will unter Umständen trotzdem, dass das Argument auch optional sein kann. Etwa in der Art: Es kann eine Passwort angegeben werden – es muss aber nicht sein. Wird dieses allerdings angegeben, so muss es aus mindestens fünf und höchstens 30 Buchstabe bestehen. Dies kann man in der Art bewerkstelligen, indem man das Argument im Controller einerseits mit einem entsprechenden Validator über die Annotation ausstattet und andererseits das Argument selbst als optional erklärt, indem man als Default-Wert NULL zuweist: Listing 16.37 Optionale validierte Argumente /** * @param string $password The password * @validate StringLength(minimum = 5, maximum = 30) */ public function someAction($password = NULL) { ... }

    347

    16 Entwicklung eines eigenen Plug-ins

    16.13 Relationen zu anderen Tabellen Wir haben nun die ganze Zeit lediglich eine Tabelle behandelt, was zwar vielleicht aufgrund der mangelnden Komplexität sehr anschaulich war, aber doch normalerweise eher selten vorkommen wird.

    16.13.1 Erweiterung des TCA Daher werden wir jetzt unsere Tabelle Blogs und eine Tabelle Posts erweitern, von der wir annehmen wollen, dass diese die Posts für jeden Blog speichern kann. Angelegt haben wir die zugehörige Datenbanktabelle bereits in Kapitel 16.4. Daher müssen wir uns nun um die TCA-Einträge in der Datei ext_tables.php kümmern. Listing 16.38 Die Datei ext_tables.php array( 'title' => array( 'config' => array( 'eval' => 'trim,required', ) ), 'posts' => array( 'config' => array( 'type' => 'inline', 'loadingStrategy' => 'proxy', 'deleteRelationsWithParent' => 1, 'foreign_class' => 'Tx_Simpleblog_Domain_Model_Post', 'foreign_table' => 'tx_simpleblog_domain_model_post', ) ), ), ); $TCA['tx_simpleblog_domain_model_post'] = array ( 'columns' => array( 'title' => array( 'config' => array( 'eval' => 'trim,required', ) ), ), ); ?>

    Gut zu erkennen ist, dass wir nun der TCA-Definition der Tabelle tx_simpleblog_ domain_model_blog eine weitere Konfiguration für ein Feld posts hinzugefügt haben, das die kommaseparierte Liste aller zugehörigen Posts zu einem Blog aufnehmen wird. Dort haben wir mittels foreign_class die zu einem Post zugehörige Domain-Klasse

    348

    16.13 Relationen zu anderen Tabellen Tx_simpleblog_Domain_Model_Post

    und die Datenbanktabelle tx_simpleblog_

    domain_model_post hinterlegt.

    16.13.2 Die Domain-Klasse Tx_Simpleblog_Domain_Model_Post Als nächstes müssen wir die Domain-Klasse anlegen – nämlich typo3conf/ext/ simpleblog/Classes/Domain/Model/Post.php: Listing 16.39 Die Datei Post.php
    /** * @var int Die UID des Blogs, zu dem der Post gehört */ protected $blogUid; /** * @var string Der Titel des Posts */ protected $title;

    /** * Setzt die UID des Blogs, zu dem der Post gehört * * @param int $blogUid Die UID des Blogs * @return void */ public function setBlogUid($blogUid) { $this->blogUid = $blogUid; } /** * Gibt die UID des Blogs, zu dem der Post gehört, zurück * * @return int Die UID des Blogs */ public function getBlogUid() { return $this->blogUid; } /** * Setter für den Titel * * @param string $title * @return void */ public function setTitle($title) { $this->title = $title; }

    349

    16 Entwicklung eines eigenen Plug-ins /** * Getter für den Titel * * @return string */ public function getTitle() { return $this->title; } } ?>

    Neben dem Titel des Posts müssen wir hier noch die UID des Blogs mitführen, da ein Post immer an ein Blog gebunden ist. Daher haben wir zusätzlich auch für die UID blogUid Getter- und Setter-Methoden in dieser Domain-Klasse.

    16.13.3 Registrieren der Actions in der Datei ext_localconf.php Damit wir überhaupt Actions mit dem ebenfalls noch zu erstellenden Post-Controller verknüpfen können, müssen diese natürlich zuerst in der Datei ext_localconf.php registriert werden. Dafür ergänzen wir diese schlicht um die Controller-Action-Kombinationen Post => add und Post => delete. Listing 16.40 Erweitern der Datei ext_localconf.php
    die ('Access denied.');

    Tx_Extbase_Utility_Plugin::configureDispatcher( $_EXTKEY, 'Pi1', array( 'Blog' => 'index,add,create,delete', 'Post' => 'index,add,create,delete' ), array( 'Blog' => 'add,create,delete', 'Post' => 'add,create,delete' ) ); ?>

    Die wirklichen neuen Einträge haben wir mit Fettschrift markiert. Zunächst verwenden wir dieselben Actions wie bei der Blog-Entity – dies kann aber später natürlich je nach Bedarf geändert werden.

    16.13.4 Erstellung des Post-Controllers Nun, da die Actions registriert sind, können wir den Post-Controller als Datei PostController.php im Verzeichnis typo3conf/ext/simpleblog/Classes/Controller/ anlegen.

    350

    16.13 Relationen zu anderen Tabellen Listing 16.41 Der Post-Controller – Teil 1
    Hiermit wird zunächst der Controller als Klasse erzeugt, indem er – wie bereits beim BlogController geschehen – die Klasse Tx_Extbase_MVC_Controller_ActionController erweitert. Listing 16.42 Der Post-Controller – Teil 2 /** * Initialisierung der aktuellen Action * * @return void */ public function initializeAction() { $this->postRepository = t3lib_div::makeInstance('Tx_Simpleblog_Domain_Repository_PostRepository') ; }

    Da wir in jeder Action auf das Post-Repository zugreifen müssen, können wir es bequem in der Methode initializeAction() instanziieren – immerhin wird diese Methode vor jeder Action aufgerufen. Listing 16.43 Der Post-Controller – Teil 3 /** * Zeigt ein Formular an, um einen Post anzulegen * * @param Tx_Simpleblog_Domain_Model_Blog $blog Der zugehörige Blog * @param Tx_Simpleblog_Domain_Model_Post $newPost Eine frische Instanz des Post-Objekts – für das Rendering * @return string Das HTML-Formular zum Anlegen des neuen Posts * @dontvalidate $newPost */ public function addAction(Tx_Simpleblog_Domain_Model_Blog $blog, Tx_Simpleblog_Domain_Model_Post $newPost = NULL) { $this->view->assign('blog', $blog); $this->view->assign('newPost', $newPost); }

    Die Add-Action schließlich wird (zusammen mit dem zugehörigen Fluid-Template) dafür sorgen, dass wir einen neuen Post eingeben können. Wichtig ist hier, dass der View nicht nur das frische, leere Post-Objekt $newPost übergeben wird, sondern auch das zugehörige Blog $blog. Würden wir darauf verzichten, dann hätte die View keinerlei Information mehr darüber, an welches Blog das Post nun eigentlich zugefügt werden muss.

    351

    16 Entwicklung eines eigenen Plug-ins Listing 16.44 Der Post-Controller – Teil 4 /** * Erzeugt einen neuen Post * * @param Tx_Simpleblog_Domain_Model_Blog $blog Der zugehörige Blog * @param Tx_Simpleblog_Domain_Model_Post $newPost Das neue PostObejekt, welches noch nicht zum Repository zugefügt wurde * @return void */ public function createAction(Tx_Simpleblog_Domain_Model_Blog $blog, Tx_Simpleblog_Domain_Model_Post $newPost) { $blog->addPost($newPost); $this->redirect('index', 'blog', NULL, array('blog' => $blog)); }

    Während wir den Blog über $this->blogRepository->add() direkt zum Repository zugefügt haben, müssen wir beim Post einen leicht anderen Weg gehen. Würden wir dies entsprechend machen, dann hätten wir zwar den Post im Repository, wir wüssten aber nicht, zu welchem Blog dieses gehört. Wie im Code ersichtlich ist, verwenden wir stattdessen eine Methode der Blog-Identiy, auf die wir gleich im Anschluss an die Behandlung des Post-Controllers und des Fluid-Templates eingehen. Der Redirect-Befehl in der vorletzten Zeile schließlich beinhaltet ebenfalls eine kleine Besonderheit. Würden wir – wie beim Blog-Controller – nur schlicht $this->redirect ('index'); schreiben, so wäre die Weiterleitung zwar auf die Action index, allerdings im Controller Post erfolgt. Zudem wäre das aktuelle Blog-Objekt wieder verschwunden. Wir wollen aber auf die Action index im Controller Blog zurückleiten und dort das aktuelle Blog-Objekt mitnehmen. Daher geben wir den Controller als zweiten Parameter und das Blog-Objekt als vierten Parameter an. Über den (hier leeren) dritten Parameter schließlich könnten wir eine ganz andere Extension anspringen, wenn wir deren Extension-Key hier vermerken. Somit können wir auch zwischen verschiedenen Extensions Daten austauschen. Listing 16.45 Der Post-Controller – Teil 5 /** * Löscht einen existierenden Post * * @param Tx_Simpleblog_Domain_Model_Blog $blog Der zugehörige Blog * @param Tx_Simpleblog_Domain_Model_Post $post Der zu löschende Post * @return void */ public function deleteAction(Tx_Simpleblog_Domain_Model_Blog $blog, Tx_Simpleblog_Domain_Model_Post $post) { $blog->removePost($post); $this->redirect('index', 'blog', NULL, array('blog' => $blog)); } } ?>

    In der Delete-Action haben wir eigentlich ähnliche Verhältnisse wie bei der Create-Action. Auch hier wird das Löschen an eine Funktion innerhalb der Entity Blog weiter delegiert, und auch der Redirect-Befehl führt den Controller explizit auf, da wir ja nach dem Löschen in der Action index des Controllers Blog landen wollen.

    352

    16.13 Relationen zu anderen Tabellen

    16.13.5 Neue Templates und Template-Änderungen Da wir in der Liste der Blogs einerseits einen Link haben wollen, über den wir neue Posts anlegen können, und andererseits bereits angelegte Posts zugehörig zu den Blogs angezeigt werden sollen (inklusive eines Links zum Löschen), müssen wir zunächst das IndexTemplate des Blogs entsprechend anpassen. Listing 16.46 Änderung in der Datei index.html des Blogs

    Blog hinzufügen



    Hier wurden nun einige neue View-Helper verwendet. Über wird eine klassische Condition eingeleitet. Wir wollen nur dann einen Code für die Posts ausgeben, wenn der zugehörige Blog auch welche besitzt. Mittels wird dieser Fall behandelt (also wenn die Condition wahr ist), und mit eben wird der Fall verwaltet, wenn die Condition nicht wahr war. In diesem Fall wird eine entsprechende Meldung ausgegeben. In der Bedingung schließlich werden die Posts – ähnlich den Blogs – per Schleife ausgelesen. Dabei greifen wir auf die Posts immer durch das Blog-Objekt hindurch zu – an diesem sind die Posts ja angehängt. So lautet das zugehörige Objekt folgerichtig blog.posts. Der Link zum Löschen des Posts verdient zudem etwas höhere Aufmerksamkeit: Dieser enthält nämlich als Argument nicht nur den aktuellen Post, sondern auch den zugehörigen Blog. Nun müssen wir aber zunächst noch das Template für diese Action erstellen, ansonsten bekommen wir beim Aufruf des Links lediglich eine weiße Seite.

    353

    16 Entwicklung eines eigenen Plug-ins Wir erstellen also zuerst das Verzeichnis typo3conf/ext/simpleblog/Resources/ Private/Templates/Post und hierin die Datei add.html mit folgendem Inhalt: Listing 16.47 Die neue Datei add.html im Verzeichnis …/Templates/Post/

    Neues Post für Blog "{blog.title}" anlegen



    Post anlegen


    16.13.6 Ändern der Blog-Identity Wir hatten vorhin festgestellt, dass es zusätzlich auch nötig ist, die Blog-Identity (d.h. die Blog-Klasse der Domäne) anzupassen, da ein Post nicht eine komplett unabhängige Einheit darstellt, sondern immer einem Blog zugeordnet wird. Listing 16.48 Änderung an der Datei Blog.php – Teil 1 /** * Die Posts, die einem Blog zugeordnet sind * * @var Tx_Extbase_Persistence_ObjectStorage * @lazy */ protected $posts;

    Hiermit wird festgelegt, dass es in einem Blog ein persistierendes Objekt (daher der Datentyp Tx_ExtbasePersistence_ObjectStorage) mit dem Namen $posts existieren soll, das die Posts aufnehmen kann. Über diese Klasse werden zudem die Funktionen attach() zum Zufügen und detach() zum Entfernen eines Posts an ein Blog eingeführt, die uns gleich begegnen werden. Eine Besonderheit ist dabei die Annoation @lazy. Diese sorgt dafür, dass das sogenannte Lazy-Loading für dieses Objekt aktiviert wird. Lazy Loading ist ein Entwurfsmuster, das besagt, dass man die Initialisierung eines Objekts auf den Zeitpunkt verschiebt, wenn man darauf zugreifen muss. Für unseren Fall bedeutet dies, dass Extbase beim Initialisieren eines Blogs zunächst nicht alle zugehörigen Posts, Kommentare und Tags gleichzeitig mitlädt, sondern wartet, bis man auf diese zugreift. Damit wird vor allem Speicherplatz gespart, und die Anwendung agiert schneller. Listing 16.49 Änderung in der Datei Blog.php – Teil 2 /** * Erzeugt einen Blog * */ public function __construct() {

    354

    16.13 Relationen zu anderen Tabellen $this->posts = new Tx_Extbase_Persistence_ObjectStorage(); }

    Hier wird noch einmal explizit das Post-Objekt initialisiert. Listing 16.50 Änderung in der Datei Blog.php – Teil 3 /** * Fügt einen Post an einen Blog an * * @param Tx_Simpleblog_Domain_Model_Post $post

    * @return void */ public function addPost(Tx_Simpleblog_Domain_Model_Post) { $this->posts->attach($post); }

    Nun kommen die Methoden der ObjectStorage-Klasse ins Spiel. Mittels attach() wird nun das Post exakt dem Blog zugewiesen, für den der Post gedacht war. Intern wird dabei (bei Verwendung einer Datenbank) in das Datenbankfeld posts der Tabelle tx_simpleblog_domain_model_blog die ID des Blogs eingetragen – also eine kommaseparierte Liste. Darum müssen Sie sich aber überhaupt nicht mehr kümmern. Ein einfaches attach() an der richtigen Stelle reicht hier, damit das Persistenz-Framework die richtigen Einstellungen für Sie trifft. Listing 16.51 Änderung in der Datei Blog.php – Teil 4 /** * Entfernt einen Post von einen Blog * * @param Tx_Simpleblog_Domain_Model_Post $post

    * @return void */ public function removePost(Tx_Simpleblog_Domain_Model_Post) { $this->posts->detach($post); }

    Das ObjectStorage-Objekt bietet ebenfalls eine Methode zum Entfernen eines Posts von einem Blog – nämlich detach(). Nun benötigen wir eigentlich an dieser Stelle nur noch eine Möglichkeit, alle Posts eines Blogs zu ermitteln – schließlich wollen wir diese ja in der Übersicht aller Blogs unter jedem Blog ausgeben: Listing 16.52 Änderungen in der Datei Blog.php – Teil 5 /** * Getter, um alle Posts eines Blogs zu erhalten *

    * @return Tx_Extbase_Persistence_ObjectStorage */ public function getPosts() { if ($this->posts instanceof Tx_Extbase_Persistence_LazyLoadingProxy) { $this->posts->_loadRealInstance(); } return clone $this->posts; }

    355

    16 Entwicklung eines eigenen Plug-ins Hier wird – wie vorhin beschrieben – geprüft, ob das Objekt mittels Lazy-Loading zwar bereits vorhanden, aber dessen Unterobjekte (die Posts) noch nicht nachgeladen wurden. Wenn diese zutrifft, werden die einzelnen Posts tatsächlich erst einmal nachgeladen. Anschließend wird ein Klon der Posts zurückgegeben (da man die Posts ja nicht verändern, sondern lediglich anzeigen will).

    16.13.7 Aufruf der Extension im Frontend Nun, da wir alle Puzzlestücke in Position gebracht haben, können wir es wagen, die Extension im Frontend aufzurufen. Wenn alles gut gegangen ist, sehen Sie eine Ausgabe ähnlich dem folgenden Screenshot:

    Abbildung 16.10 Ausgabe der Extension Simpleblog mit Erweiterung um Posts

    16.14 Relationen zu anderen Tabellen m:n Im vorherigen Kapitel haben wir gesehen, wie man eine Relation zu einer anderen Tabelle aufbaut. Dafür haben wir eine eigene Tabelle angelegt und in der ursprünglichen Tabelle ein Feld für die Datensätze der neuen Tabelle eingebaut. In diesem Feld nun werden die UIDs der Datensätze der neuen Tabelle als kommaseparierte Liste gespeichert. Dies hat prinzipiell zwei Nachteile – einerseits ist das Handling von solchen Datensätzen viel komplizierter, da man beispielsweise den Inhalt des Feldes zunächst als String erhält und diesen parsen muss, um auf die einzelnen Werte zugreifen zu können. Andererseits ist die Methode auch sehr Zeitaufwendig und damit Resourcenhungrig, da man erst alle Da-

    356

    16.14 Relationen zu anderen Tabellen m:n tensätze der ursprünglichen Tabelle durchsuchen muss, um beispielsweise alle Datensätze zu ermitteln, in denen ein bestimmter Wert in der neuen Tabelle gesetzt ist. Zusätzlich widerspricht die Speicherung als kommaseparierter Liste auch den Regeln der Normalisierung bei relationalen Tabellen, Datenbank-Tabellen Daher werden wir für die Realisierung der Tags (Bezichner, die einem Post zugeordnet sind) auf eine m:n-Tabelle zurückgreifen. Wir haben ja bereits die benötigten Tabellen tx_simpleblog_domain_model_tag und tx_simpleblog_post_tag_mm über die Datei ext_tables.sql in Kapitel 16.4 angelegt. Letztere Tabelle nimmt nun unsere m:n-Daten auf – je Zeile wird dafür sowohl die UID des Posts wie auch die UID des Tags gespeichert. TCA-Einträge Ebenfalls in Kapitel 16.4 haben wir bereits die Datei typo3conf/ext/simpleblog/ Configuration/TCA/TCA.php entsprechend für die Verwendung der m:n-Tabelle vorbereitet. Beachten Sie hierzu, dass TYPO3 m:n-Relationen leicht fälschlicherweise als MM bezeichnet – die Bedeutung ist aber die selbe. Dafür zuständig sind die folgenden Zeilen in der Datei TCA.php: Listing 16.53 Für die m:n-Relation zuständiger Ausschnitt aus der Datei TCA.php $TCA['tx_simpleblog_domain_model_tag'] = array( 'ctrl' => $TCA['tx_simpleblog_domain_model_tag']['ctrl'], 'columns' => array( … 'posts' => array( … 'config' => array( … 'MM' => 'tx_simpleblog_post_tag_mm', 'MM_opposite_field' => 'tags', 'MM_insert_fields' => array('tablenames' => 'tx_simpleblog_domain_model_tag'), 'MM_match_fields' => array('tablenames' => 'tx_simpleblog_domain_model_tag'), ) ), ) );

    Controller/Action-Kombinationen registrieren Wir wollen, dass es möglich ist, einen Tag anzulegen. Dafür brauchen wir wieder zwei Actions – eine Add-Action mit der das Formular angezeigt wird, in welches man das Tag eingeben kann und eine Create-Action, die dafür verwendet wird, das Tag zu persistieren. Damit wir diese beiden Actions überhaupt verwenden können, müssen wir diese vorher in der Datei ext_localconf.php registrieren. Dafür fügen wir die hervorgehobenen Zeilen hinzu:

    357

    16 Entwicklung eines eigenen Plug-ins Listing 16.54 Registrierung der Controller/Action-Kombinationen in der Datei ext_localconf.php Tx_Extbase_Utility_Extension::configurePlugin( $_EXTKEY, 'Pi1', array( 'Blog' => 'index,add,create,edit,update,delete', 'Post' => 'index,add,create,delete', 'Tag' => 'add,create' ), array( 'Blog' => 'index,add,create,edit,update,delete', 'Post' => 'index,add,create,delete', 'Tag' => 'add,create' ) );

    Das Tag-Model Bevor wir nun anfangen, die Controller und Views für den Tag erstellen, benötigen wir zunächst das Model für den Tag. Dieses ist sehr einfach, da wir lediglich einen Namen vergeben wollen. Für das Model legen wir nun die Datei typo3conf/ext/simpleblog/ Classes/Domain/Model/Tag.php an: Listing 16.55 Das Tag-Model (Tag.php) name = $name; } /** * Setter für den Namen * * @param string $name * @return void */ public function setName($name) { $this->name = $name; } /** * Getter für den Namen * * @return string Der Name des Tags */

    358

    16.14 Relationen zu anderen Tabellen m:n public function getName() { return $this->name; } /** * Wandelt den Namen in einen String um * * @return string */ public function __toString() { return $this->getName(); } } ?>

    Der Tag-Controller Jetzt, nachdem der Controller registriert ist, können wir diesen aufbauen. Dafür legen wir die Datei typo3conf/ext/simpleblog/Classes/Controller/TagController.php an: Listing 16.56 Erstellung des Tag-Controllers postRepository = t3lib_div::makeInstance('Tx_Simpleblog_Domain_Repository_PostRepository') ; } /** * Stellt das Formular dar, welches das Tag aufnimmt * * @param Tx_Simpleblog_Domain_Model_Post $post * @param Tx_Simpleblog_Domain_Model_Tag $newTag * @return string Das HTML-Formular um den neuen Tag einzugeben * @dontvalidate $newTag */ public function addAction(Tx_Simpleblog_Domain_Model_Post $post, Tx_Simpleblog_Domain_Model_Tag $newTag = NULL) { $this->view->assign('post', $post); $this->view->assign('newTag', $newTag); } /** * Diese Action persistiert den Tag und leitet weiter auf die * Action index des Blog-Controllers * * @param Tx_Simpleblog_Domain_Model_Post $post Der zugehörige Post * @param Tx_Simpleblog_Domain_Model_Tag $newTag Der neue Tag * @return void

    359

    16 Entwicklung eines eigenen Plug-ins */ public function createAction(Tx_Simpleblog_Domain_Model_Post $post, Tx_Simpleblog_Domain_Model_Tag $newTag) { $post->addTag($newTag); $this->flashMessages->add('Your new Tag was created.'); $this->redirect('index', 'Blog', NULL, NULL); } /** * Überschreibe getErrorFlashMessage um * eine entsprechende Flash-Message zu erhalten * * @return string */ protected function getErrorFlashMessage() { switch ($this->actionMethodName) { case 'createAction' : return 'Could not create the new tag:'; default : return parent::getErrorFlashMessage(); } } } ?>

    Hier gibt es erst einmal nicht viel Neues zu entdecken. Wir benötigen das Post-Repository, da wir ja den Tag direkt einem Post zuordnen wollen. Daher wird das Repository in der Initialize-Action zur Verfügung gestellt. In der Create-Action weisen wir sowohl den aktuellen Post wie auch den neuen (noch leeren) Tag dem View zu (welchen wir gleich anschließend behandeln). Schließlich sorgt die Create-Action dafür, dass der Tag dem Post zugeordnet wird und leitet anschließend auf die Index-Action des Blog-Controllers um. Der View für die Add-Action des Tag-Controllers Um den Tag entsprechend eingeben zu können, benötigen wir einen Add-View den wir in Form der Datei typo3conf/ext/simpleblog/Resources/Private/Templates/Tag/ add.html anlegen. Listing 16.57 Der Add-View des TagControllers (add.html)

    Einen neuen Tag fuer Post "{post.title}" anlegen





    Anpassen des Index-Views des Blog-Controllers Nun müssen wir nur noch den Index-View des Blog-Controllers anpassen, damit wir überhaupt eine Möglichkeit haben, den Tag anzugeben und vor allem anzuzeigen. Dazu ändern wir typo3conf/ext/simpleblog/Resources/Private/Template/Blog/index.html und fügen folgenden hervorgehobenen Code hinzu:

    360

    16.15 Mehrsprachigkeit zufügen Listing 16.58 Änderungen am Index-View des Blog-Controllers (index.html) …
    • <strong>Blog: {blog.title} ( Edit Blog / Add Post / Delete Blog )

      Test

      • {post.title} ( Delete Post / Add Post-Tag ) Tags: {tag}

      Keine Posts vorhanden!!



    Über den Link-ViewHelper haben wir nun einen Link angelegt, welcher das Formular aufruft, um den tag einzugeben und über die darauf folgende Schleife geben wir bereits eingegebene Tags wieder aus.

    16.15 Mehrsprachigkeit zufügen Bislang haben wir alle Ausgaben hart kodiert. In multilingualen Installationen ist dies natürlich keine Option – hier muss man die Möglichkeit haben, mehrsprachige Labels verwenden zu können. Prinzipiell wird für die Bereitstellung der Mehrsprachigkeit ein bereits unter TYPO3 4.x übliches XML-Sprachfile verwendet. Dieses trägt den Namen locallang.xml und liegt im Verzeichnis typo3conf/ext/simpleblog/Resources/Private/Language/. Diese Datei füllen wir nun mit dem folgenden Inhalt:

    361

    16 Entwicklung eines eigenen Plug-ins Listing 16.59 Inhalt der Datei locallang.xml <meta type="array"> module <description>Sprachlabels für die Simpleblog Extension

    Wir haben hier je drei Labels für zwei Sprachen definiert. Mit der Angabe wird die Default-Sprache definiert – d.h. wenn keine andere spezielle Sprache im TYPO3 konfiguriert wurde oder wenn eine eventuell konfigurierte Sprache in den Sprachfiles nicht wieder gefunden wird. Über wird nun speziell die deutsche Sprache definiert. Letztlich kann man beliebige Bezeichner (Keys) für die Labels verwenden – wir empfehlen, einen strukturellen Namen zu verwenden. So haben wir hier beispielsweise blog.containpost verwendet, um darauf hinzudeuten, dass der Bezeichner im Umfeld der Blogs verwendet wird. Um nun darauf zuzugreifen, wird ein eigener View-Helper translate im Template verwendet – hier das Template index.html des Blogs: Listing 16.60 Änderung in der Blog-Template-Datei index.html …



    Testen können Sie Ihr Setup sehr einfach, indem Sie in das Root-Template im Bereich Setup den folgenden TypoScript-Code platzieren:

    362

    16.16 Konfiguration mittels TypoScript Listing 16.61 TypoScript-Setup zur Änderung der Sprache config.language = de

    16.16 Konfiguration mittels TypoScript TypoScript ist eine mächtige und komplexe Konfigurationsmöglichkeit, die zwar in TYPO3 4.x zur Verfügung steht – unter anderem aber TYPO3 5.x nicht mehr bzw. nicht in dieser Form. Selbstverständlich ist zwar geplant, TypoScript auch in TYPO3 5.x zur Verfügung zu stellen – sicher ist dies zum jetzigen Zeitpunkt allerdings nicht. Und so kann es passieren, dass Ihre Extension – wenn sie denn auf TypoScript basiert – mehr Änderung als normal benötigt, wenn Sie diese für FLOW3 (respektive TYPO3 5.x) fit machen wollen. Prinzipiell werden die TypoScript-Konfiguration im Pfad plugin.tx_extensionname ausgewertet. Dabei wird hier der Extension-Name in Kleinbuchstaben und ohne Underscores „_“ notiert. Extbase wertet dabei den folgenden TypoScript-Baum aus: Listing 16.62 TypoScript-Konfiguration // Das grundlegende TS-Element plugin.tx_extensionname { // Einstellungen für ganze Extensions. Diese sind unter // $this->settings im Controller und als {settings} // im FLUID-Template zugänglich. Hier kommen Ihre individuellen // Einstellungen hinein. settings { … } // Hier werden die Einstellungen für die Persistenzschicht getroffen // beispielsweise die storagePid persistence { // Automatisches Cache-Löschen aktivieren (1 - default) oder // deaktivieren enableAutomaticCacheClearing = 1 // StoragePid (siehe Kapitel 16.19.1) storagePid = … // Einstellungen für das Speichern neuer Datensätze classes { // kompletter Klassenname des Domain-Models [KlassenName] { // ID der Seite, auf der die neuen Datensätze gespeichert // werden sollen newRecordStoragePid = … mapping { // Name der Kind-Klasse (kann entweder per TCA // …['config']['foreign_class'] oder hier definiert werden tableName = … // Angabe der zu mappenden Spalten columns = … } } }

    363

    16 Entwicklung eines eigenen Plug-ins } // Einstellungen für den View und die Templates view { … } // Überschreiben der Lokalisierungen (Sprachlabels) _LOCAL_LANG { … } }

    Um nun ein solches TypoScript zur Verfügung zu haben, werden wir das folgende in der Datei typo3conf/ext/simpleblog/Configuration/TypoScript/setup.txt speichern: Listing 16.63 TypoScript setup.txt für die Extension simpleblog plugin.tx_simpleblog { settings { key1 = value1 key2 = value2 key1.key3 = value3 controllers { Blog { maxItems = 5 } } } }

    Damit das TypoScript nun auch geladen werden kann, erweitern wir die Datei ext_tables.php um die folgende Ladeanweisung: Listing 16.64 Ladeanweisung für das statische TypoScript t3lib_extMgm::addStaticFile($_EXTKEY, 'Configuration/TypoScript', 'Blog Example');

    Durch diese Anweisung werden sowohl eine Datei setup.txt wie auch eine Datei constants.txt in dem angegebenen Verzeichnis spezifiziert, sofern diese vorhanden sind. Um diese nun in das System zu laden, müssen Sie im Modul Web > Template auf Ihr TypoScript-Template im Seitenbaum klicken und über Click here to edit whole template record den Datensatz bearbeiten. Im Tab Includes nun finden Sie im Abschnitt Include static (from extensions): auf der rechten Seite das statische Template. Mit einem Klick darauf und durch anschließendes Löschen des Caches ist dieses eingebunden.

    364

    16.17 Backend-Module mit Extbase

    Abbildung 16.11 Zufügen des statischen Templates (vor dem Klick)

    Um nun darauf zuzugreifen, gehen wir in den Blog-Controller zur Index-Action und geben dort das TypoScript-Settings-Array testweise komplett aus, um zu sehen, wie die Konfiguration dort genau ankommt: Listing 16.65 Ausgabe des Settings-Arrays in der Index-Action des Blog-Cotrollers … public function indexAction($name = 'Test') { debug($this->settings); $this->view->assign('blogs', $this->blogRepository->findAll()); …

    Abbildung 16.12 Ausgabe des Debug-Statements

    Nun könnten wir diese Konfigurationen direkt verwenden – beispielsweise: Listing 16.66 Zugriff auf die Settings-Konfiguration if ($this->settings['key2'] == 'value2') { … }

    16.17 Backend-Module mit Extbase Extbase eignet sich nicht nur hervorragend, um Frontend-Plug-ins zu entwickeln – es ist natürlich auch möglich, damit Backend-Module zu entwerfen. Dies ist insofern eine revolutionäre Neuerung – war es doch bislang nicht möglich, im Backend ein vernünftiges Templating zu verwenden –, sodass zahlreiche Backend-Module die komplette Ausgabe in den Programmcode geschrieben haben.

    365

    16 Entwicklung eines eigenen Plug-ins Hierfür sind grundsätzlich einige zusätzliche Schritte notwendig. Zunächst muss das Backend-Modul in der Datei ext_tables.php registriert werden: Listing 16.67 Registrierung des Backend-Moduls in der Datei ext_tables.php … if (TYPO3_MODE === 'BE') { /** * Hiermit wird ein Backend-Modul registriert */ Tx_Extbase_Utility_Extension::registerModule( $_EXTKEY, 'web', // Modul soll Submodul von 'web' sein 'tx_simpleblog_m1', // Der Key des Submoduls '', // Position innerhalb des Moduls 'web' array( // Angabe sämtlicher Controller-Action // Kombinationen, die im Backend // erlaubt sein sollen 'Blog' => 'index,add,create,edit,update,delete', 'Post' => 'index,add,create,delete,addTag,createTag', 'Tag' => 'add,create' ), array( // Angabe von Zugriffsrechten, Icons // und Label-Datei 'access' => 'user,group', 'icon' => 'EXT:blog_example/ext_icon.gif', 'labels' => 'LLL:EXT:' . $_EXTKEY . '/Resources/Private/Language/locallang_mod.xml', ) ); /** * Hinzufügen eines Labels für die Context-Sensitive-Hilfe (CSH) */ t3lib_extMgm::addLLrefForTCAdescr('_MOD_web_BlogExampleTxBlogexampleM1', 'EXT:' . $_EXTKEY . '/Resources/Private/Language/locallang_csh.xml'); }

    Im obigen Beispiel wird schlicht das gesamte bislang aufgebaute Simpleblog-Beispiel zusätzlich ins Backend verfrachtet, sodass man nun auch dort die Blogs, Posts und Tags anlegen kann. Selbstverständlich würde man dies bei einem reellen Beispiel trennen, sodass man eigene Backend-Actions eben nur im Backend registriert und damit aufrufbar macht.

    16.18 Der Extbase-Kickstarter Gerade wenn Sie die letzten 100 Seiten dieses dritten Teils des Buches abgearbeitet haben, werden Sie sich sicherlich des Öfteren gefragt haben, ob man davon nicht einige Schritte automatisieren kann – beispielsweise in einer Art Kickstarter. Ingmar Schlecht hat tatsächlich im Sommer 2009 im Rahmen des Google Summer of Code (GSoC) damit begonnen, einen derartigen Kickstarter zu schreiben. Dieser ist bei Drucklegung zwar schon prinzipiell verwendbar – steckt aber noch komplett in den Kinderschuhen und verfügt daher über relativ wenige Features. Dies ist auch ein Grund, warum wir dieses Kapitel so weit hinten platziert haben. Wenn Sie die grundsätzliche Funktionsweise von

    366

    16.18 Der Extbase-Kickstarter Extbase von Grund auf erlernen, werden Sie es später auch leicht haben, den Kickstarter zu verwenden, um Ihnen Arbeit abzunehmen. Andersherum funktioniert dies natürlich nicht. Da wir denken, dass gerade bei den neuen Paradigmen wie MVC ein grundsätzliches Verständnis der Materie vonnöten ist, kann es nicht schaden, einen derartigen Kickstarter erst zu verwenden, wenn die Zusammenhänge komplett klar sind. Da zwischen Manuskriptabgabe und Erscheinen eines Buches bisweilen mehrere Monate liegen, kann es gut sein, dass der Extbase-Kickstarter dann bereits final veröffentlicht wurde und auch über andere oder tiefer gehende Features verfügt wie die, die wir Ihnen im Folgenden zeigen wollen. Aber sehr wahrscheinlich werden Sie nach der Lektüre dieses Buches leicht damit umgehen können. Wenn Sie trotzdem auf Probleme damit stoßen sollten – schreiben Sie uns einfach eine E-Mail: [email protected] – wir helfen Ihnen dann gerne.

    16.18.1 Installation des Extbase-Kickstarters Da der Extbase-Kickstarter zur Drucklegung noch nicht Teil des TYPO3-4.3-Pakets ist, muss dieser zunächst manuell aus dem Forge-SVN installiert werden.Die Adresse dabei lautet https://svn.typo3.org/TYPO3v4/Extensions/extbase_kickstarter/trunk. Unter Linux und Mac OS X können Sie einfach auf der Kommandozeile mittels svn co Adresse die Sourcen aus dem SVN auschecken. Unter Windows können Sie das Programm Tortoise verwenden. Nun kopieren Sie die Sourcen in ein Verzeichnis mit dem Namen extbase_kickstarter (wenn nicht schon bereits geschehen) und diesen Ordner in das Verzeichnis typo3conf/ ext/. Nun taucht der Extbase-Kickstarter im Extension-Manager unter Install Extensions bereits auf, und Sie können diesen mittels Klick auf das Plussymbol am Anfang direkt installieren.

    Abbildung 16.13 Die Extension Extbase-Kickstarter im Extension-Manager

    Nun erscheint im linken Funktionenmenü im Abschnitt Admin (da der Extbase-Kickstarter natürlich nur für Administratoren zugänglich ist) der Eintrag Extbase Kickstarter als letztes Element.

    367

    16 Entwicklung eines eigenen Plug-ins

    Abbildung 16.14 Der Extbase-Kickstarter im Admin tools-Menü

    Klicken Sie nun auf den Extbase-Kickstarter.

    16.18.2 Überblick über den Arbeitsbereich Im nächsten Bild sehen Sie die vier grundsätzlichen Bereiche, die wir im Folgenden kurz anreißen wollen, bevor wir weiter in die Modellierung einsteigen: 1. Wenn Sie auf den Link unter 1 klicken, schiebt sich von links ein Bereich herein, in dem Sie die Extension-Konfiguration durchführen können. Dazu gehören der Name der Extension, der Extension-Key, der Status (alpha, beta, stable) und die Personen, die daran mitgewirkt haben. Wenn Sie die Extension nicht alleine entwickeln, sondern mit einem Team an Leuten, dann können Sie diese hier alle verewigen. 2. Auf der rechten Seite haben Sie ebenfalls einen Bereich, der Code Generator genannt wird. Zur Drucklegung war dieser Bereich allerdings noch leer und hatte keine Funktion. Gut denkbar, dass hier in Zukunft Möglichkeiten zur Steuerung der Codegenerierung zu finden sind. 3. Im unteren Bereich können Sie das erstellte Model abspeichern und wieder laden, ein neues anlegen sowie ein bestehendes löschen. Die momentane Codegenerierung findet beispielsweise erst statt, wenn Sie Ihr Modell abspeichern – darauf kommen wir in Kürze zu sprechen. 4. Hier schließlich startet die Modellierung. Das wird uns gleich im folgenden Unterkapitel beschäftigen.

    368

    16.18 Der Extbase-Kickstarter

    Abbildung 16.15 Der Extbase-Kickstarter in seiner ganzen Pracht

    16.18.3 Eingabe der Extension-Konfiguration Wir klicken nun also auf den Button für die Extension-Konfiguration (Nummer 1 in der vorhergehenden Abbildung). Nun können wir folgende Daten eingeben:

    „ Einen aussagekräftigen Namen „ Den Extension-Key (wir haben hier natürlich einen beliebigen anderen genommen, damit uns keine Extension überschrieben wird)

    „ Eine kurze Beschreibung „ Den Status (hier development) „ Einen Ansprechpartner – dieser kann über add angelegt werden ƒ Dort den Namen, die Rolle, E-Mail und Firma (sofern vorhanden) eintragen. – Teile dieser Informationen finden sich dann im Quellcode beispielsweise wieder. Nun sollte man zunächst erst einmal unten auf den Buttons Save klicken und warten, bis das Popup dies mit Saved! bestätigt hat.

    369

    16 Entwicklung eines eigenen Plug-ins

    Abbildung 16.16 Eingabe der Extension Konfiguration

    16.18.4 Modellierung Nun können wir mit der Modellierung starten. Zu Demonstrationszwecken und auch um einen Vergleich zu haben, wollen wir nun das Simpleblog-Beispiel 1:1 nachmodellieren, d h., wir benötigen ein Blog mit einem Titel, das kann Posts beinhalten (die ebenfalls einen Titel haben), und die Posts können mit einem Tag versehen werden (der einen Namen hat und zudem über eine M:M-Relation verknüpft ist). Gehen Sie nun mit der Maus auf die in Abbildung 16.15 unter Punkt 4 gezeigte grünblaue Fläche mit der Aufschrift New Model Object, klicken Sie mit der linken Maustaste darauf und ziehen nun nach unten auf die freie Fläche darunter, ohne die Maustaste währenddessen loszulassen. Wenn Sie nun mit der Maus stehen bleiben und die Maustaste loslassen, wurde ein neues Objekt erzeugt.

    Abbildung 16.17 Ein neues Objekt wurde erzeugt.

    370

    16.18 Der Extbase-Kickstarter 16.18.4.1 Anlegen des Blog-Objekts Über einen Klick auf click to edit können Sie nun einen Namen für das Objekt vergeben – wir nehmen hier zunächst als Namen Blog. Über Klick auf OK wird der Name übernommen. Nun füllen wir die restlichen Elemente aus:

    „ Bei Domain Object Settings wählen wir als Object Type Entity. Sie könnten hier auch Value Object auswählen, wenn Ihr Object einem solchen entsprechen würde. Zusätzlich wählen Sie Is aggregate root aus, da Sie wollen, dass Ihr Blog ein eigenes Repository erhält. Als Description könnten Sie nun auch eine kurze Beschreibung wie Das ist das Blog hinterlegen.

    „ Sie können nun bei Default Actions eine Reihe von Actions auswählen, die Sie gerne vorbereitet hätten. Bei Drucklegung des Buches war dieser Bereich allerdings noch ohne Funktion, sodass Sie die Controller samt Actions später manuell anlegen mussten.

    „ Nun folgen die Properties (Eigenschaften), die Sie mittels Add zufügen können. ƒ Als Property Name geben Sie den Namen der Eigenschaft an – bei uns Title. Der Property Type ist Text String. Als Description könnten Sie beispielsweise Titel des Blogs eingeben. Und da Sie sicherstellen sollten, dass das Attribut auch eingegeben wird, wählen Sie die Checkbox Is Required aus.

    „ Schließlich brauchen Sie noch eine Relation, die Sie über Add im Abschnitt Relations anlegen können.

    ƒ Hier tragen Sie als Name

    Posts ein und als Type 0..* (foreign Key). Diese Einstellung entspricht der kommaseparierten Liste im Feld Posts.

    Abbildung 16.18 Das Blog-Objekt

    371

    16 Entwicklung eines eigenen Plug-ins

    16.18.5 Anlegen des Post-Objekts Letztlich wiederholen wir hier exakt die Schritte des vorherigen Kapitels, mit einer Ausnahme:

    „ Die Relation zu Tags ist vom Typ 0..*

    (aasociation table).

    Abbildung 16.19 Das Post-Objekt

    16.18.6 Anlegen des Tag-Objekts Auch hier halten wir uns wieder an die vorherige Anleitung mit dem Unterschied, dass wir hier keine Relation benötigen und der Bezeichner nicht Title, sondern Name heißt und das Objekt kein Aggregate Root ist.

    Abbildung 16.20 Das Tag-Objekt

    372

    16.18 Der Extbase-Kickstarter

    16.18.7 Relationen festlegen Nun, da wir alle drei relevanten Objekte positioniert haben, können wir die Relationen zwischen diesen festlegen. Dies erfolgt sehr einfach und vor allem sehr elegant. Gehen Sie mit der Maus auf den runden Kreis rechts neben dem Label Related Object im Post-Objekt. Nun klicken Sie darauf und lassen den Finger auf der linken Maustaste. Wenn Sie nun die Maus bewegen, sehen Sie eine blaue Pipe (eine dicke Linie), die sich mit Ihrer Maus mitbewegt. Nun bewegen Sie die Maus auf das Post-Objekt in die linke obere Ecke gleich rechts vom TYPO3-Logo – dort ist ebenfalls ein runder Kreis. Genau über diesem lassen Sie nun die Maustaste los. Jetzt sollten die beiden Objekte verbunden sein, und die Verbindung – die sogenannte Pipe – bewegt sich auch mit, wenn Sie die Objekte verschieben. Zusätzlich sehen Sie den Text [wired] rechts neben dem Kreis im Abschnitt Relations. Dasselbe wiederholen Sie mit den Posts – auch hier greifen Sie den Kreis im Relationsbereich des Post-Objects und ziehen diesen auf den Kreis in der linken oberen Ecke des TagObjekts. ACHTUNG – die Reihenfolge ist entscheidend Die Reihenfolge – also von welchem Element zu welchem Element Sie die Pipe ziehen – ist entscheidend. Versuchen Sie es genau andersherum, wie eben beschrieben, bekommen Sie eine Fehlermeldung und können Ihre Extension nicht abspeichern.

    Abbildung 16.21 Fertig verknüpftes Domain-Model

    373

    16 Entwicklung eines eigenen Plug-ins Nun können Sie das Domain-Model über die Schaltfläche Save unten in der Mitte abspeichern.

    Abbildung 16.22 Durch den Extbase-Kickstarter angelegte Verzeichnisse und Dateien

    Wie Sie gut erkennen können, hat der Extbase-Kickstarter ganze Arbeit geleistet und nahezu alle wichtigen Verzeichnisse und Dateien angelegt. Lediglich die Controller fehlen bei Drucklegung noch – es ist aber damit zu rechnen, dass diese bei Erscheinen des Buches ebenfalls einwandfrei erzeugt werden. Aber selbst jetzt können Sie diese leicht durch das bisher erworbene Wissen anlegen. Eine Datei verdient eine besondere Hervorhebung – kickstarter.json. Diese ist dafür zuständig, dass eine derart erzeugte Extension mit dem Extbase-Kickstarter wieder gelesen und damit bearbeitet werden kann. Aber wie es bei Kickstarten so ist – dieser erzeugt den Code stets neu. Haben Sie also in den Dateien eine manuelle Änderung vorgenommen, so werden diese Änderungen beim erneuten Abspeichern allesamt wieder überschrieben. Legen Sie sich also in jedem Fall eine Sicherung davon an.

    16.19 Weitere Extbase-Interna In diesem Kapitel sollen die Interna – meist Kleinigkeiten – aufgelistet werden, für die ein eigenes Kapitel zu überdimensioniert wäre.

    16.19.1 StoragePid Jeder Datensatz liegt in TYPO3 auf einer einzelnen Seite. Diese Seite wiederum hat eine Seiten-ID, die als PID (Parent-ID) bezeichnet wird. Damit hat also auch jeder Datensatz eine ebensolche PID und ist damit einer speziellen Seite zuzuordnen.

    374

    16.19 Weitere Extbase-Interna Nun gibt es in Extbase einige Fälle, bei denen die Storage PID ermittelt werden muss:

    „ Schreiben „ Aktualisieren „ Lesen 16.19.1.1 Schreibzugriffe Sobald Extbase versucht, Datensätze (beispielsweise neue Blog- oder Post-Einträge) abzuspeichern, muss ermittelt werden, welche PID diese bekommen sollen – dabei geht Extbase wie folgt vor: 1. Existiert der folgende TS-Schlüssel, so wird dieser verwendet: plugin. tx_[pluginName]. persistence. classes. [KlassenName]. newRecordStoragePid

    Dabei ist anstelle von [pluginName] der komplette, kleingeschriebene Plug-in-Name ohne Unterstriche und anstelle von [KlassenName] der komplette Class-Name der Model-Datei einzugeben. 2. Existiert der folgende TS-Schlüssel, so wird dieser verwendet: config. tx_extbase. persistence. classes. [KlassenName]. newRecordStoragePid

    Dabei ist anstelle von [KlassenName] der komplette Class-Name der Model-Datei einzugeben. 3. Existieren beide TS-Schlüssel nicht, wird der Datensatz auf der Seite mit der Weltkugel mit per PID = 0 gespeichert. 16.19.1.2 Aktualisieren Beim Aktualisieren wird die PID natürlich per Default nicht verändert. Wird also ein Datensatz auf der PID=5 angelegt, so wird dieser nach dem Aktualisieren dort auch wieder abgespeichert. Allerdings kann man den Speicherort in der Domäne verändern, wenn das Model die Eigenschaft $pid und entsprechende Getter- und Setter-Methoden hat. Damit ist es also möglich, einen Datensatz von einer Seite auf eine andere Seite zu verschieben.

    375

    16 Entwicklung eines eigenen Plug-ins 16.19.1.3 Lesen Auch das Lesen von Datensätzen gehorcht eigenen Regeln – diese werde wie folgt durchlaufen: 1. Falls das Feld Startingpoint im Backend der Plug-in-Konfiguration gesetzt ist, werden alle Datensätze, die dieses Plug-in betreffen, von der dort hinterlegten Seite geladen. 2. Wenn dies nicht der Fall ist, wird nach dem folgenden TS-Key gesucht und der dort hinterlegte Wert als PID verwendet: plugin. tx_[pluginName]. persistence. storagePid

    Dabei ist anstelle von [pluginName] der komplette, kleingeschriebene Plug-in-Name ohne Unterstriche einzugeben. 3. Wenn dies auch nicht der Fall ist, wird nach dem folgenden TS-Key gesucht und der dort hinterlegte Wert als PID verwendet: config. tx_extbase. persistence. storagePid

    4. Ist dies schließlich auch nicht der Fall, so wird für die PID der Wert 0 verwendet.

    16.19.2 MVC-Request Nach einem MVC-Request landet man in einem Controller in einer bestimmten Action. Nun wäre es aber sehr brauchbar, wenn man auf genau diese Daten gesammelt zugreifen könnte. Dies ist über das $this->request-Objekt möglich. Die Eigenschaften dieses Objekts sind alle protected – insofern ist nur ein Zugriff mittels Getter möglich. Für alle Eigenschaften existieren aber natürlich entsprechende Getter – dafür müssen Sie einfach get vor die Eigenschaft stellen und den ersten Buchstaben der Eigenschaft großschreiben. Beispielsweise erreichen Sie die Eigenschaft pluginName über den Getter $this->request->getPluginName(). Tabelle 16.1 Eigenschaften des Request-Objekts

    376

    Eigenschaft

    Beispielwert

    Beschreibung

    format

    html

    Das Ausgabeformat

    method

    GET

    Die Übertragungsmethode (GET oder POST)

    requestURI

    http://….index.php?id=…

    Die komplette URL des Requests

    baseURI

    http://127.0.0.1:8080/

    Die URL des Hosts

    16.19 Weitere Extbase-Interna Eigenschaft

    Beispielwert

    Beschreibung

    controllerObjectNamePattern

    Tx_@extension_Controller_ @controllerController

    Die Vorlage zum Ermitteln des Controllers

    pluginName

    Pi1

    Der Name des Plug-ins

    controllerExtensionName

    Simpleblog

    Der Name der Extension

    controllerName

    Blog

    Der Name des Controllers

    controllerActionName

    index

    Der Name der Action

    arguments

    Array ( [blog] = 65 [action] = index [controller] = Blog

    Alle GET-/POST-Parameter als Array

    ) dispateched

    1

    errors

    Angabe, ob der Request durch den Dispatcher geschickt wurde Alle Fehlermeldungen als Array

    16.19.3 FlashMessages realisieren Häufig möchte man den User über das informieren, was die Software im Hintergrund macht. Beispielsweise sind Statusmeldungen ein probates Mittel, um dem User mitzuteilen, was die letzte Action gerade gemacht hat. So präsentiert man z.B. ein Formular zum Anlegen eines Blog-Posts, und nach dem Abschicken wechselt man (im Erfolgsfall) per redirect() zur Index-Action, die die Liste aller Blogs anzeigt. Wenn nun auf dieser Seite ganz oben ein Hinweis in der Art „Ihr Post wurde erfolgreich angelegt“ angezeigt werden würde, wäre dies ein Schritt hin zu einer perfekten Benutzerführung. Der User wäre somit stets informiert, was die letzte Aktion gerade bewirkt hat. Sogenannte FlashMessages lösen diese Aufgabenstellung perfekt. Dazu gehört, dass man einen FlashMessage-Speicher zu jeder Zeit mit Nachrichten befüllen und diese dann gebündelt an einer anderen Stelle wieder ausgeben kann. Für das Handling von FlashMessages stehen Ihnen folgende Methoden zur Verfügung:

    „

    $this->flashMessages->add($message)

    Zufügen einer Nachricht zum FlashMessage-Speicher

    „

    $this->flashMessages->getAll()

    Ermittelt alle Nachrichten aus dem FlashMessage-Speicher und gibt diese zurück

    „

    $this->flashMessages->getAllAndFlush()

    Ermittelt alle Nachrichten aus dem FlashMessage-Speicher, gibt diese zurück und löscht den Speicher anschließend

    „

    $this->flashMessages->flush()

    Löscht den FlashMessage-Speicher

    377

    16 Entwicklung eines eigenen Plug-ins

    „

    $this->flashMessages->persist()

    Normalerweise werden alle FlashMessages spätestens nach Ablauf des Skriptes wieder aus dem Speicher gelöscht. Diese Methode sorgt dafür, dass die FlashMessages in der Session des Users gespeichert werden und somit zu einem späteren Zeitpunkt immer noch vorhanden sind.

    „

    $this->flashMessages->loadFlashMessagesFromSession()

    Hiermit werden die mittels persist() gespeicherten FlashMessages wieder aus der Session des Users ins Objekt geholt und stehen wieder über die obigen Befehle zur Verfügung.

    378

    17 17 Die Fluid-Template-Engine In einem MVC gesteuerten System kommt natürlich der View auch eine ganz besondere Aufmerksamkeit zuteil. Gerade durch die Trennung von Businesslogik und Darstellung, muss die View dieses auch ideal widerspiegeln können. Per Default besitzt die View lediglich eine sehr einfache Methode render() – die allerdings über keinerlei zusätzliche Funktionalitäten verfügt. Daher war schnell klar, dass man an dieser Stelle eine eigene Template-Engine benötigt, die sich perfekt in das MVC-Framework einpasst und zudem leistungsfähig genug ist, um auch komplizierteste Anwendungsfälle abdecken zu können. Zudem – und auch dies war eine Hauptforderung – sollte es möglich sein, dass Templates auch von einem Nichtprogrammierer (wie einem Designer) erstellt bzw. geändert werden können. Weiterhin sollte keine allzu exotische Sprache für das Template ersonnen werden, da diese ansonsten mit keiner der üblicherweise eingesetzten IDEs in irgendeiner Weise kompatibel gewesen wäre. Sebastian Kurfürst, der Erfinder von Fluid, hat im Herbst 2008, direkt nach den Berliner Transition Days, bestehende Systeme wie das klassische herkömmliche TYPO3 Templating (basierend auf Marker und Subparts), PHPTAL oder Smarty untersucht und verglichen und daraufhin schnell festgestellt, dass diese Systeme aus dem einen oder anderen Gründen nicht den Anforderungen entsprechen. Entweder musste man zur Verwendung über Programmierkenntnisse verfügen (wie dies beim klassischen System der Fall ist), oder man war zu eingeschränkt in den Möglichkeiten. Nachteile der klassischen Template-Engine

    „ „ „ „

    Basiert lediglich auf Marker und Subparts – dadurch überhaupt nicht flexibel genug Es gibt keinerlei Ablaufsteuerung (Control Flow) Keine Erweiterbarkeit Arbeitet ausschließlich mit Arrays – aber nicht mit Objekten

    379

    17 Die Fluid-Template-Engine Nachteile von Smarty

    „ Basiert immer noch auf PHP4 „ Hat eine eigene proprietäre Syntax, die auf geschweiften Klammern beruht – damit ist keine Auto-Completion in einer IDE möglich

    „ Die eingebauten Funktionen verfügen nicht über Namespaces – dadurch sind Kollisionen mit eigenen Funktionen möglich. Nachteile von PHPTAL

    „ Das Markup basiert zwar auch wohlgeformtem XML – es ist aber nicht valide. „ Die Semantik ist sehr intuitiv und gewöhnungsbedürftig. „ PHP wird innerhalb des Templates verwendet – dadurch Trennung von Designer und Programmierer nicht haltbar

    „ Keine Möglichkeit der Auto-Completion in IDEs „ Nur schwer mittels eigener Funktionen zu erweitern Die Lösung Und so war (wieder einmal) die Idee geboren, eine speziell angepasste und alle Vorteile vereinende Template-Engine selbst zu schreiben. Diese wurde vorläufig mit dem Arbeitstitel BEER3 bezeichnet – dann aber durch eine Community-Befragung in Fluid geändert. Fluid ist angetreten, um folgende Vorteile zu realisieren:

    „ Die Templating-Engine soll einfach und elegant zu verwenden sein. „ Es soll ein einfaches und klares Interface existieren, mit dem man die Engine leicht erweitern kann.

    „ Es sollen unterschiedliche Ausgabeformate – nicht nur HTML – damit unterstützt werden.

    „ 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.

    17.1

    Vorbereitung Damit es möglich ist, die nachfolgenden Beispiele allesamt gleich auszuprobieren, müssen wir zunächst eine kleine Testumgebung aufsetzen. Dafür installieren wir einerseits natürlich die Extensions extbase und fluid (die aber, wenn Sie das vorherige Kapitel verfolgt haben, bereits installiert sein sollten) und die Extension efempty (Extbase Fluid Empty) aus dem Extension Respository, die eine „leere“ Extension

    380

    17.1 Vorbereitung mit einem rudimentären Model Start (mit einer Eigenschaft title), einem Controller (StartController), einer Action (index) und einem dazugehörigen View zur Verfügung stellt. Nun platzieren Sie die Extension auf einer Seite, indem Sie das Content-Element-Plug-in auswählen und als Plug-in-Typ Empty Extbase/Fluid Container angeben.

    Abbildung 17.1 Einfügen der Extension efempty als Content-Element

    Wenn Sie nun die Extension im Frontend aufrufen, sollten Sie die folgende Ausgabe bekommen:

    Abbildung 17.2 Ausgabe der Extension efempty im Frontend

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

    381

    17 Die Fluid-Template-Engine

    17.2

    Basissyntax und einfache Ausgabe In der Controller-Datei – genauer in der Index-Action – wird nun über die Methode assign() ein Wert an die View übergeben: Listing 17.1 Einfache Zuweisung mittels assign() im Start-Controller public function indexAction() { … $this->view->assign('helloworld','Hello world!'); … }

    Die entscheidende Stelle haben wir hier 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 erreichbar sein soll, und über den ersten wird der Wert selbst spezifiziert. In unserem Beispiel wird also der Wert Hello world! an den Bezeichner helloworld übergeben. Nun betrachten wir uns die View-Datei, um zu sehen, wie das nun verarbeitet und ausgegeben wird: Listing 17.2 Ausgabe des Bezeichners helloworld in der View-Datei index.html

    This is the efempty extension!

    It works :-)

    {helloworld}

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

    17.2.1

    Arrays

    Um nun beispielsweise Arrays auszugeben, verwendet Fluid die sogenannte Punktsyntax (oder auch Objektzugriffssyntax). So kann über den Bezeichner mit dem Punkt auf den numerischen Index von Array zugegriffen werden. In den folgenden Codebeispielen notieren wir der Übersichtlichkeit halber oben die entscheidenden Codezeilen in der Action und darunter den Code im Template. Listing 17.3 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}

    382

    17.2 Basissyntax und einfache Ausgabe Man kann also über die Punktschreibweise ganz einfach auf den numerischen Index zugreifen. Natürlich funktioniert dies ähnlich für einen assoziativen Index: Listing 17.4 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}

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

    17.2.2

    Objekte

    Da Fluid grundsätzlich objektorientiert arbeitet, ist auch in diesem Bereich gute Unterstützung zu erwarten. Und tatsächlich sind das Ansprechen eines Objekts bzw. dessen Eigenschaften im Grunde exakt identisch zur Ansprache als Array. Listing 17.5 Anspache von Arrays // 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}

    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, und die wiederum wird mit dem Setter setTitle gesetzt. Nun wird also das ganze Objekt samt gesetztem Titel an die View übergeben, und 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 – d h., ein direkter Zugriff darauf ist somit natürlich nicht möglich. Daher wird auch in obigem Fall die Eigenschaft nicht direkt ausgelesen, sondern es wird stattdessen der im Model vorhandene Getter getTitle() verwendet. Und auch hier 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.

    383

    17 Die Fluid-Template-Engine Das ist schon alles, was Sie über die Basissyntax von Fluid wissen müssen – das ist der sogenannte Core (auch Kern genannt). Alle weiteren Funktionalitäten werden von sogenannten ViewHelpern übernommen, die wir uns im Folgenden näher ansehen.

    17.3

    Fluid ViewHelper-Syntax Sämtliche Ausgabelogik nun wird von speziellen Tags, den sogenannten ViewHelpern, übernommen. Diese sorgen für Kontrollstrukturen, Schleifen, Links und ähnliche benötigte Funktionalitäten. Die Grundsyntax dabei ist wie folgt (beachten Sie, dass das kleine f und der Doppelpunkt sozusagen den Namensraum von Fluid angeben):

    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 beispielsweise den If-ViewHelper, der mit 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 den 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 über die folgende Datei verwaltet: typo3conf/ext/fluid/Classes/ViewHelpers/Link/ActionViewHelper.php

    17.3.1

    Namespace (Namensraum)

    Wir haben nun immer ein kleines f: als Namensraum verwendet. Dies ist aber nicht ohne Grund so, sondern wurde implizit definiert. In jedem Fluid-Template kann man nämlich den verwendeten Namensraum mit folgendem Bezeichner definieren: Listing 17.6 Default-Deklaration des Fluid-Namensraumes {namespace f=Tx_Fluid_ViewHelpers}

    Hier wird festgelegt, dass das kleine f letztlich immer als Abkürzung für Tx_Fluid_ViewHelper steht. Damit wird dann auch klar, warum bei f:for beispielsweise die Klasse Tx_Fluid_ViewHelper_For angesprochen wird.

    384

    17.3 Fluid ViewHelper-Syntax Sie können selbstverständlich auch Ihren eignen Namensraum festlegen – beispielsweise wenn Sie eigene ViewHelper schreiben. Dies wird uns in einem späteren Kapitel natürlich auch noch begegnen. Lassen Sie die Namespace-Deklaration weg, wird automatisch die obige DefaultDeklaration verwendet.

    17.3.2

    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 Listing 17.7 Einige Beispiele für Argumente // über den Parameter arguments wird ein Argument übergeben ... read more // zwei oder mehr Argumente sind natürlich ebenfalls möglich Edit

    17.3.2.1

    Boolesche Ausdrücke

    Beispielsweise kann dem If-ViewHelper ein Argument übergeben, das vom Typ Boolean sein soll und darauf geprüft wird. Die boolesche Bedingung wird dabei wie folgt behandelt:

    „ Ist der übergebene Wert vom Typ Boolean (TRUE oder FALSE), wird dieser verwendet. „ Ist der übergebene Wert vom Typ String (eine Zeichenkette) und ist der String false oder leer, so wird der boolesche Wert FALSE verwendet.

    „ Ist der übergebene Wert vom Typ Numeric (eine natürliche Zahl) und ist diese Zahl größer als null, so wird TRUE verwendet.

    „ Ist der übergebene Wert vom Typ Array oder ist es ein Countable Object (zählbares Objekt), dann wird TRUE verwendet, wenn es mindestens ein Element oder Objekt enthält.

    385

    17 Die Fluid-Template-Engine

    „ Ist der übergebene Wert vom Typ Object (ist es also ein Objekt), so wird TRUE zurückgegeben.

    „ In allen anderen Fällen wird FALSE zurückgegeben. 17.3.2.2

    Komplexe boolesche Ausdrücke

    Oftmals kann es nötig sein, auch komplexere boolesche Ausdrücke zu evaluieren. Stellen wir uns einfach einmal vor, dass wir eine Art Menü haben, das alle verfügbaren Seiten anzeigt. Nun soll aber die aktuell ausgewählte Seite im Menü etwas hervorgehoben werden. Dies könnte man vielleicht wie folgt notieren: Listing 17.8 Beispiel für einen komplexen booleschen Ausdruck <strong>…

    Hier wird mit dem Operator == geprüft, ob die Seite in der Schleife identisch mit der aktuellen Seite ist, und wenn dem so ist, wird die Ausgabe mit einem <strong>-Tag hervorgehoben. Ein komplexer boolescher Ausdruck kann dabei das folgende Format haben: Ausdruck1 Operator Ausdruck 2

    Als Operatoren sind dabei folgende zugelassen:

    „ „ „ „ „ „

    == (identisch) > (größer) >= (größer oder gleich) < (kleiner) <= (kleiner oder gleich) % (Modulo)

    Die beiden Ausdrücke Ausdruck1 und Ausdruck2 können dabei vom folgenden Typ sein:

    „ Eine Zahl (Integer oder Float) „ Ein JSON-Array „ Ein ViewHelper ƒ ...

    „ Ein Objekt-Zugriffspfad (dies wird der übliche Zugriff sein) 17.3.2.3

    Arrays

    Für einige ViewHelper (wie dem SelectViewHelper, der eine Select-Box erzeugt) ist es notwendig, ein Array als Argument zu übergeben. Die grundlegende Syntax orientiert sich wieder an der JSON-Syntax. Dabei steht auf der linken Seite der Eigenschaft jeweils ein

    386

    17.4 ViewHelper-Übersicht Bezeichner, für den ausschließlich Buchstaben zugelassen sind. Auf der rechten Seite dagegen kann Folgendes stehen:

    „ Eine Nummer ƒ {a : 1, b : 2, c „ Ein String ƒ {a : 'String1',

    : 3}

    b : "Ein zweiter String – hier müssen doppelte Anführungszeichen \" escapet werden, aber nicht einfache ' Anführungszeichen"}

    „ Ein (verschachteltes) Array ƒ {a : {

    a1 : "String1", a2 : "String2"

    }, b : "String3" }

    „ Eine Objekt-Zugriffspfad ƒ {a1 : objekt1.eigenschaft1,

    17.4

    a2: objekt2}

    ViewHelper-Übersicht In Fluid sind, wie bereits erwähnt, zahlreiche nützliche ViewHelper bereits eingebaut. Im Folgenden finden Sie eine alphabetische Übersicht über alle verfügbaren ViewHelper, die mit der Installation der Extension fluid mitgeliefert werden. Sollten Ihnen diese nicht ausreichen, so können Sie ohne großen Aufwand eigene ViewHelper erstellen – dies wird im nachfolgenden Kapitel näher beleuchtet.

    17.4.1

    alias

    Mit dem ViewHelper alias können Sie einen neuen Namen vergeben. Dies ist insbesondere dann hilfreich, wenn Sie damit längere Objektpfade abkürzen. Nehmen wir einmal an, der Objektpfad objekt.eigenschaft1.eigenschaft2 hat den Wert wert1. Dann gibt der folgende ViewHelper genau zweimal diesen Wert aus, da beide Zugriffe identisch sind: Listing 17.9 ViewHelper alias

    Verwendung {x.eigenschaft2} ist das selbe wie {y}

    387

    17 Die Fluid-Template-Engine

    // Ausgabe wert1 ist das selbe wie wert1

    Tabelle 17.1 Argumente für den ViewHelper alias Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    map

    array

    ja

    -

    -

    17.4.2

    base

    Dieser ViewHelper sorgt für ein -Tag. Dabei kann kein Argument übergeben werden – der Inhalt des Tags ist immer die aktuelle Domain. Listing 17.10 ViewHelper base

    // Verwendung // Ausgabe

    17.4.3

    cObject

    Über diesen ViewHelper ist es möglich, ein TypoScript-Objekt auszugeben. Mit dem Argument typoscriptObjectPath können Sie dabei den Pfade spezifizieren und über currentValueKey den entsprechenden Namen desjenigen Schlüssels, der als current-Wert verwendet werden soll. Listing 17.11 ViewHelper cObject // TypoScript-Setup lib.test = TEXT lib.test.value = Diese Ausgabe kommt vom TypoScript! // Verwendung im Template Test // Ausgabe Diese Ausgabe kommt vom TypoScript!

    Tabelle 17.2 Argumente für den ViewHelper cObject

    388

    Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    typoscriptObjectPath

    string

    ja



    Wenn der Pfad nicht existiert, wird eine Fehlermeldung ausgegeben.

    currentValueKey

    string

    nein





    17.4 ViewHelper-Übersicht

    17.4.4

    count

    Mit diesem ViewHelper (der lediglich ein Wrapper der PHP-Funktion count() ist), kann die Anzahl von Elementen eines Objekts oder Arrays zurückgegeben werden. Listing 17.12 ViewHelper count // Verwendung // Ausgabe 2

    Tabelle 17.3 Argumente für den ViewHelper count Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    subject

    array

    ja



    Array oder ObjectStorage, in dem die Elemente gezählt werden

    17.4.5

    cycle

    Über diesen ViewHelper lässt sich eine sogenannte Rotation zwischen vorgegebenen Werten erzeugen. Damit kann man beispielsweise iterierende Zeilen realisieren, da diese immer zwischen einem Wert für die gerade Zeile und einem anderen Wert für die ungerade Zeile wechseln. Natürlich sind auch Rotationen mit mehr als zwei Elementen möglich. Listing 17.13 ViewHelper cycle // Verwendung
    • {values} - {alternate}
    // Ausgabe
    • 1 - odd
    • 2 - even
    • 3 - odd
    • 4 - even
    • 5 - odd


    In diesem Beispiel wird über die For-Schleife (dem For-ViewHelper) das Array mit den fünf Werten durchgegangen. Innerhalb dieser Schleife entscheidet der Cycle-ViewHelper, welcher Wert jeweils dafür existieren soll. Da dort nur die zwei Werte odd und even existieren, wird jedem Schleifenwert erst odd und dann dem nächsten even zugeordnet. Der übernächste bekommt wieder odd und so weiter. Hätte Cycle drei Werte gehabt, so wären

    389

    17 Die Fluid-Template-Engine die ersten zwei Werte doppelt zugeordnet worden und der dritte nur einmal im ersten Durchlauf, da ja nur fünf Elemente aus der For-Schleife existieren. Tabelle 17.4 Argumente für den ViewHelper cycle Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    as

    string

    ja



    Der Name für die iterierende Variable, auf die innerha b des ViewHelpers zugegriffen werden kann

    values

    array

    ja



    Das Array oder SplObjectStorage, über das iteriert wird

    17.4.6

    debug

    Dieser ViewHelper ist ein Wrapper für die TYPO3-eigene Debug-Funktion. Dabei können Sie als Argument einen Titel zur besseren Übersichtlichkeit ausgeben lassen. Listing 17.14 ViewHelper debug // Verwendung {object}

    Tabelle 17.5 Argumente für den ViewHelper debug Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    title

    string

    nein





    17.4.7

    else

    Der Else-ViewHelper macht nur in Zusammenhang mit dem If-ViewHelper Sinn und wird dementsprechend dort behandelt.

    17.4.8

    for

    Der For-ViewHandler ist genau genommen (noch) ein Foreach-ViewHandler, da dieser letztlich 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 dabei auf den Schlüssel zugreifen.

    390

    17.4 ViewHelper-Übersicht Listing 17.15 ViewHelper For // Verwendung {wert} // Ausgabe 1ab4 // Verwendung
    • {label}: {wert}
    //Ausgabe
    • key1:
    • key2:
    • key3:
    • key4:


    name1 name2 name3 name4

    Tabelle 17.6 Argumente für den ViewHelper for Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    each

    array

    ja



    Das Array, über das iteriert wird

    as

    string

    ja



    Der Name der Iterationsvariablen

    key

    string

    nein



    Der Name der Variablen, die den Schlüssel speichert

    17.4.9

    form

    Über den Form-ViewHelper können umfangreiche Formulare aufgebaut werden. Prinzipiell wird zuerst das Form-Tag selbst definiert und darin die verschiedenen Eingabefelder. Listing 17.16 ViewHelper form // Verwendung // Ausgabe


    391

    17 Die Fluid-Template-Engine Wie gut zu erkennen ist, wird die Formular-Action auf die URL der aktuellen Seite gesetzt – einschließlich der Angabe für den (Default-)Controller. Als Formular-Methode wird per Default POST verwendet. Zusätzlich befinden sich aber noch drei Hidden-Felder, die sowohl die Extension, den Controller wie auch die Action spezifizieren. Diese Angaben sind unter Umständen notwendig, wenn man nach dem Abschicken noch Zugriff auf den Referrer benötigt. Tabelle 17.7 Argumente für den ViewHelper form

    392

    Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accesskey

    string

    nein



    Keyboard-Shortcut

    action

    string

    nein

    Die aktuelle URL

    Die Form-Action

    additionalAttributes

    array

    nein



    Hiermit können zusätzliche Attribute für das
    -Tag angegeben werden

    arguments

    string

    nein



    Zusätzliche Argumente

    class

    string

    nein



    CSS-Klasse(n)

    controller

    string

    nein

    Der aktuelle Controller

    Der Controller, der nach dem Abschicken aufgerufen werden soll

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    enctype

    string

    nein



    MIME-Type für die Übertragung

    extensionName

    string

    nein

    Die aktuelle Extension

    Der Extension-Name ohne tx und unterstrichen

    id

    string

    nein



    ID des Formulars

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    method

    string

    nein

    post

    POST oder GET

    name

    string

    nein



    Der Name der Form

    onreset

    string

    nein



    JS-Handler für onReset

    onsubmit

    string

    nein



    JS-Handler für onSubmit

    pageUid

    integer

    nein

    Die aktuelle Page-UID

    Die ID der Seite, zu der gesprungen werden soll

    pluginName

    string

    nein

    Das aktuelle Plug-in

    Der Plug-in-Name

    object

    mixed

    nein



    Ein Form-Objekt

    options

    array

    nein



    Typolink-Optionen

    pageType

    integer

    nein

    0

    Der Seitentyp (typenum)

    style

    string

    nein



    Inline CSS Styles

    17.4 ViewHelper-Übersicht Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    tabindex

    integer

    nein



    TabIndex-Angabe

    title

    string

    nein



    Title-Attribut (Tooltipp-Text)

    17.4.9.1

    form.checkbox

    Diesen ViewHelper können Sie zur Erzeugung einer Checkbox verwenden. Listing 17.17 ViewHelper form.checkbox // Verwendung // Ausgabe // Verwendung – Vorausgefüllt, wenn object.name == 5 ist // Verwendung – Binden an die Objekt-Eigenschaft "interessen"

    Tabelle 17.8 Argumente für den ViewHelper form.checkbox Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accesskey

    string

    additionalAttributes

    array

    nein



    Keyboard-Shortcut

    nein



    Hiermit können zusätzliche Attribute für das -Tag angegeben werden

    checked

    string

    nein



    Wenn auf den Wert checked gesetzt, dann ist die Checkbox vorausgewählt.

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    disabled

    string

    nein



    Deaktiviert die Checkbox

    id

    string

    nein



    ID des Formulars

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    name

    string

    nein



    Der Name der Selectbox

    property

    string

    nein



    Name der Objekteigenschaft, wenn im Form-Tag das objectAttribut verwendet wurde. Dann werden hier das name- und value-Attr but ignoriert.

    style

    string

    nein



    Inline CSS Styles

    393

    17 Die Fluid-Template-Engine Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    tabindex

    integer

    nein



    TabIndex-Angabe

    title

    string

    nein



    Title-Attribut (Tooltipp-Text)

    17.4.9.2

    form.error

    Alle Fehler, die in Zusammenhang mit dem Form-ViewHelper auftreten, können über diesen ViewHelper angezeigt werden: Listing 17.18 ViewHelper form.error // Verwendung
    {error.message}

    <strong>{error.propertyName}: {error.code}: {errorDetail.message}

    // Ausgabe
    Validation errors for argument "newPost"

    <strong>newPost: 1245107351: Validation errors for property "title"



    Tabelle 17.9 Argumente für den ViewHelper form.error Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    as

    string

    nein

    error

    Der Name des Fehlerobjekts (so wie dieses dann in Fluid angesprochen werden kann)

    for

    string

    ja



    Der Name der Objekteigenschaft (wird später auch Objekte aufnehmen können)

    17.4.9.3

    form.hidden

    Über diese ViewHelper können sogenannte Hidden-Fields in Formulare eingebracht werden.

    394

    17.4 ViewHelper-Übersicht Listing 17.19 ViewHelper form.hidden // Verwendung // Ausgabe

    Gut zu sehen ist, dass das Name-Attribut automatisch in den Namensraum der Extension (tx_efempty_pi1) verschoben wird. Tabelle 17.10 Argumente für den ViewHelper form.hidden Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accesskey

    string

    nein



    Keyboard-Shortcut

    additionalAttributes

    array

    nein



    Hiermit können zusätzliche Attribute für das -Tag angegeben werden.

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    id

    string

    nein



    ID des Formulars

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    name

    string

    nein



    Der Name des Feldes

    property

    string

    nein



    Name der Objekteigenschaft, wenn im Form-Tag das object-Attribut verwendet wurde. Dann werden hier das name- und value-Attribut ignoriert.

    style

    string

    nein



    Inline CSS Styles

    tabindex

    integer

    nein



    TabIndex-Angabe

    title

    string

    nein



    Title-Attr but (Tooltipp-Text)

    value

    string

    nein



    Der Wert des Input-Tags

    17.4.9.4

    form.password

    Input-Boxen, deren Eingabe verschleiert ist (sodass man diese nicht sehen kann und stattdessen * angezeigt wird), können mit diesem ViewHelper erzeugt werden. Listing 17.20 ViewHelper form.password // Verwendung // Ausgabe

    395

    17 Die Fluid-Template-Engine Tabelle 17.11 Argumente für den ViewHelper form.password Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accesskey

    string

    nein



    Keyboard-Shortcut

    additionalAttributes

    array

    nein



    Hiermit können zusätzliche Attribute für das -Tag angegeben werden.

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    disabled

    string

    nein



    Deaktiviert die Checkbox

    id

    string

    nein



    ID des Formulars

    lang

    string

    nein



    Language-Attr but (RFC 1766)

    maxlength

    string

    nein



    Die maximale Länge des Passwortes

    name

    string

    nein



    Der Name des Feldes

    property

    string

    nein



    Name der Objekteigenschaft, wenn im Form-Tag das objectAttribut verwendet wurde. Dann werden hier das name- und value-Attribut ignoriert.

    readonly

    string

    nein



    Ist dieser Wert gesetzt, kann das Passwort nicht mehr geändert werden.

    style

    string

    nein



    Inline CSS Styles

    tabindex

    integer

    nein



    TabIndex-Angabe

    title

    string

    nein



    Title-Attribut (Tooltipp-Text)

    value

    string

    nein



    Der Wert des Input-Tags

    17.4.9.5

    form.radio

    Über diesen ViewHelper können Sie Radio-Buttons realisieren. Listing 17.21 ViewHelper form.radio // Verwendung // Ausgabe // Verwendung – Vorausgefüllt, wenn object.name == 5 ist // Verwendung – Binden an die Objekt-Eigenschaft "newsletter" ja nein

    396

    17.4 ViewHelper-Übersicht Tabelle 17.12 Argumente für den ViewHelper form.radio Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accesskey

    string

    nein



    Keyboard-Shortcut

    additionalAttributes

    array

    nein



    Hiermit können zusätzliche Attribute für das -Tag angegeben werden.

    checked

    string

    nein



    Wenn auf den Wert checked gesetzt, dann ist die Checkbox vorausgewählt.

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    disabled

    string

    nein



    Deaktiviert die Checkbox

    id

    string

    nein



    ID des Formulars

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    name

    string

    nein



    Der Name der Selectbox

    property

    string

    nein



    Name der Objekteigenschaft, wenn im Form-Tag das objectAttribut verwendet wurde. Dann werden hier das name- und value-Attr but ignoriert.

    style

    string

    nein



    Inline CSS Styles

    tabindex

    integer

    nein



    TabIndex-Angabe

    title

    string

    nein



    Title-Attr but (Tooltipp-Text)

    17.4.9.6

    form.select

    Select-Boxen können mithilfe dieses ViewHelpers bequem erzeugt werden. Sie können dabei entweder selbst definierte Werte verwenden oder sogar Objekteigenschaften als Werte verwenden. Selbst definierte Werte Listing 17.22 ViewHelper form.select – selbst definierte Werte // Verwendung // Ausgabe <select name="tx_efempty_pi1[Farben]">

    397

    17 Die Fluid-Template-Engine Über das Attribut selectedValue="blau" beispielsweise könnte man nun die blaue Farbe vorauswählen. Objekteigenschaften als Werte Listing 17.23 ViewHelper form.select – Objekteigenschaften als Werte // Verwendung

    In diesem Fall wird ein Objekt mit dem Namen userArray übergeben, das sämtliche User enthält. Diese haben eine Eigenschaft vorname, die als Label ausgegeben wird. Der dazugehörige Wert wiederum ist die UID des Users. Beide Werte (sowohl das Label wie auch der Wert) werden natürlich nicht direkt ermittelt, sondern über die zugehörigen Getter-Methoden. In obigen Beispiel wird also für den Wert die Funktion $user->getId() bemüht und für das Label die Funktion $user->getVorname(). Tabelle 17.13 Argumente für den ViewHelper form.select

    398

    Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accesskey

    string

    nein



    Keyboard-Shortcut

    additionalAttributes

    array

    nein



    Hiermit können zusätzliche Attribute für das -Tag angegeben werden.

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    id

    string

    nein



    ID des Formulars

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    multiple

    string

    nein



    Erlauben, ob mehrere Felder ausgewählt werden können

    name

    string

    nein



    Der Name des Feldes

    options

    array

    nein



    Entweder ein Objekt oder ein assoziatives Array der Werte, getrennt mit einem Doppelpunkt. Der erste Wert wird als Schlüssel verwendet, der zweite als Label.

    optionLabelField

    string

    nein



    Wenn bei options ein Objekt angegeben wurde, werden hier über eine Getter-Funktion die Label ermittelt.

    17.4 ViewHelper-Übersicht Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    optionValueField

    string

    nein



    Wenn bei options ein Objekt angegeben wurde, werden hier über eine Getter-Funktion die Werte ermittelt.

    property

    string

    nein



    Name der Objekteigenschaft, wenn im Form-Tag das objectAttribut verwendet wurde. Dann werden hier das name- und value-Attr but ignoriert.

    size

    integer

    nein



    Größe (Höhe) der Select-Box

    style

    string

    nein



    Inline CSS Styles

    tabindex

    integer

    nein



    TabIndex-Angabe

    title

    string

    nein



    Title-Attribut (Tooltipp-Text)

    value

    string

    nein



    Der Wert des Input-Tags

    17.4.9.7

    form.submit

    Dieser ViewHelper wird zur Erzeugung eines Submit-Buttons verwendet. Listing 17.24 ViewHelper form.submit // Verwendung Abschicken //Ausgabe

    Tabelle 17.14 Argumente für den ViewHelper form.submit Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accesskey

    string

    nein



    Keyboard-Shortcut

    additionalAttributes

    array

    nein



    Hiermit können zusätzliche Attribute für das -Tag angegeben werden.

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    id

    string

    nein



    ID des Formulars

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    name

    string

    nein



    Der Name des Feldes

    style

    string

    nein



    Inline CSS Styles

    tabindex

    integer

    nein



    TabIndex-Angabe

    399

    17 Die Fluid-Template-Engine Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    title

    string

    nein



    Title-Attribut (Tooltipp-Text)

    value

    string

    nein



    Der Wert des Input-Tags

    17.4.9.8

    form.textarea

    Über diesen ViewHelper ist es möglich, ein Textarea-Feld in einem Formular zu erzeugen. Listing 17.25 ViewHelper form.textarea // Verwendung // Ausgabe

    Tabelle 17.15 Argumente für den ViewHelper form.textarea

    400

    Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accesskey

    string

    nein



    Keyboard-Shortcut

    additionalAttributes

    array

    nein



    Hiermit können zusätzliche Attribute für das -Tag angegeben werden.

    class

    string

    nein



    CSS-Klasse(n)

    cols

    integer

    nein



    Anzahl der Spalten

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    id

    string

    nein



    ID des Formulars

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    name

    string

    nein



    Der Name des Feldes

    property

    string

    nein



    Name der Objekteigenschaft, wenn im Form-Tag das objectAttribut verwendet wurde. Dann werden hier das name- und value-Attribut ignoriert.

    rows

    integer

    nein



    Anzahl der Zeilen

    style

    string

    nein



    Inline CSS Styles

    tabindex

    integer

    nein



    TabIndex-Angabe

    title

    string

    nein



    Title-Attribut (Tooltipp-Text)

    value

    string

    nein



    Der Wert des Input-Tags

    17.4 ViewHelper-Übersicht 17.4.9.9

    form.textbox

    Mit diesem View-Helper erzeugen Sie eine klassische Input-Textbox in Ihrem Formular. Listing 17.26 ViewHelper form.textbox // Verwendung // Ausgabe

    Tabelle 17.16 Argumente für den ViewHelper form.textbox Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accesskey

    string

    nein



    Keyboard-Shortcut

    additionalAttribues

    array

    nein



    Hiermit können zusätzliche Attribute für das -Tag angegeben werden.

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    id

    string

    nein



    ID des Formulars

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    name

    string

    nein



    Der Name des Feldes

    property

    string

    nein



    Name der Objekteigenschaft, wenn im Form-Tag das objectAttribut verwendet wurde. Dann werden hier das name- und value-Attr but ignoriert.

    style

    string

    nein



    Inline CSS Styles

    tabindex

    integer

    nein



    TabIndex-Angabe

    title

    string

    nein



    Title-Attr but (Tooltipp-Text)

    value

    string

    nein



    Der Wert des Input-Tags

    17.4.9.10 form.upload Mithilfe dieses ViewHelpers können Sie Upload-Formulare realisieren, mit denen es möglich ist, Dateien auf den Server zu übertragen. Wichtig ist dabei, dass das Formular selbst die Option enctype="multipart/formdata" gesetzt hat!

    401

    17 Die Fluid-Template-Engine Listing 17.27 ViewHelper form.upload // Verwendung // Ausgabe

    Tabelle 17.17 Argumente für den ViewHelper form.upload Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accesskey

    string

    nein



    Keyboard-Shortcut

    additionalAttributes

    array

    nein



    Hiermit können zusätzliche Attribute für das -Tag angegeben werden.

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    id

    string

    nein



    ID des Formulars

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    name

    string

    nein



    Der Name des Feldes

    property

    string

    nein



    Name der Objekteigenschaft, wenn im Form-Tag das objectAttribut verwendet wurde. Dann werden hier das name- und value-Attribut ignoriert.

    style

    string

    nein



    Inline CSS Styles

    tabindex

    integer

    nein



    TabIndex-Angabe

    title

    string

    nein



    Title-Attribut (Tooltipp-Text)

    value

    string

    nein



    Der Wert des Input-Tags

    17.4.10 format Die ViewHandler in dieser Gruppe sind primär dafür zuständig, einen Eingabewert in ein anderes Format umzuwandeln. 17.4.10.1 format.crop Dieser ViewHandller dient dazu, um einen Text nach einer bestimmten Anzahl von Zeichen abzuschneiden. Listing 17.28 ViewHandler format.crop // Verwendung Das ist ein sehr langer Text!

    402

    17.4 ViewHelper-Übersicht

    // Ausgabe Das ist ein... // Verwendung Das ist ein sehr langer Text! // Ausgabe Das ist ein [mehr]

    Über die Eigenschaft maxCharacters wird die maximale Anzahl an Buchstaben bestimmt, die ausgegeben werden dürfen. Der ViewHelper bricht die Darstellung unterhalb dieser Grenze ab und fügt drei Punkte hinzu, es sei denn, es ist mittels des Arguments append ein anderer String definiert. Tabelle 17.18 Argumente für den ViewHandler format.crop Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    append

    string

    nein



    String, der an der Stelle zugefügt wird, an der abgeschnitten wird

    maxCharacters

    integer

    ja



    Stelle, an der abgeschnitten wird

    respectWordBoundaries

    boolean

    nein

    1

    Schneidet nur an Wortgrenzen ab

    17.4.10.2 format.currency Wandelt die Eingabe in einen Währungs-String um. Listing 17.29 ViewHelper format.currency // Verwendung 123456 // Ausgabe 1.234.567,00 €

    Tabelle 17.19 Argumente für den ViewHandler format.currency Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    currencySign

    string

    nein



    Das Währungssymbol

    decimalSeparator

    string

    nein



    Trennzeichen für die Dezimal-Darstellung

    thousandsSeparator

    string

    nein



    Trennzeichen für die Tausender-Darstellung

    403

    17 Die Fluid-Template-Engine 17.4.10.3 format.date# Hiermit stehen Ihnen alle Möglichkeiten der Datumsformatierung zur Verfügung. Dabei wird als Eingabe ein DateTime-Objekt erwartet. Ebenso möglich ist eine Berechnung – ausgehend vom aktuellen Datum. Listing 17.30 ViewHelper format.date // Verwendung now // Ausgabe 23.09.2009 - 22:28:27 // Verwendung +1 week 2 days 4 hours 2 seconds // Ausgabe 03.10.2009 - 02:27:09

    Mögliche Eingabe- und Formatierungsparameter sind auf der folgenden Seite beschrieben: http://www.php.net/manual/en/function.strtotime.php

    Tabelle 17.20 Argumente für den ViewHandler form.date Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    format

    string

    nein

    Y-m-d

    Hiermit wird die Formatierung festgelegt.

    17.4.10.4 format.html Dieser ViewHandler rendert einen String gemäß der in TYPO3 festgelegten Funktion parseFunc. Es ist über das Argument parseFuncTSPath möglich, eine eigene parseFuncFunktion zu hinterlegen. Listing 17.31 ViewHandler format.html // Verwendung String1 String2. Das ist ein Link. // Ausgabe

    String1 String2. Das ist ein Link.



    Tabelle 17.21 Argumente für den ViewHandler format.html

    404

    Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    parseFuncTSPath

    string

    nein

    lib.parseFunc_RTE

    Pfad zu einer eigenen parseFunc-Funktion

    17.4 ViewHelper-Übersicht 17.4.10.5 format.nl2br Dies ist letztlich nur ein Wrapper für die PHP-Funktion nl2br(). Listing 17.32 ViewHandler format.nl2br // Verwendung String1 String2 String3 // Ausgabe String1
    String2
    String3

    17.4.10.6 format.number Hierüber kann eine Eingabe als Zahl formatiert werden – inkl. Dezimalstellen und Tausender-Trennzeichen. Listing 17.33 ViewHandler format.number // Verwendung 12345.678 // Ausgabe 12.345,7

    Tabelle 17.22 Argumente für den ViewHelper format.number Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    decimals

    int

    nein

    2

    Stellen nach dem Komma

    decimalSeparator

    string

    nein

    ,

    Trennzeichen für die Dezimal-Darstellung

    thousandsSeparator

    string

    nein

    .

    Trennzeichen für die Tausender-Darstellung

    17.4.10.7 format.padding Dieser ViewHandler dient als Wrapper für die PHP-Funktion str_pad(). Es wird der Eingabestring genommen und mit dem als padString spezifizierten Zeichen (es können dort auch mehrere definiert werden) so lange hinten aufgefüllt, bis die unter padLength angegebene Länge erreicht ist. Listing 17.34 ViewHandler format.padding // Verwendung TYPO3 // Ausgabe TYPO3...............

    405

    17 Die Fluid-Template-Engine Tabelle 17.23 Argumente für den ViewHandler format.padding Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    padLength

    integer

    nein



    Länge des auszugebenden Strings

    padString

    string

    nein



    Der String, mit dem aufgefüllt wird

    17.4.10.8 format.printf Hierüber wird ein ViewHelper definiert, der die Eingabe mittels printf() formatiert. Als Eingabe können Sie entweder ein Array oder einen einzelnen Wert übergeben. Alle möglichen Formatierungsmöglichkeiten finden Sie hier: http://www.php.net/manual/en/function.sprintf.php

    Listing 17.35 ViewHelper format.printf // Verwendung %s und %s sind super! // Ausgabe Fluid und TYPO3 sind super!

    Tabelle 17.24 Argumente für den ViewHandler format.printf Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    arguments

    array

    ja



    Die Argumente für printf

    17.4.11 groupedFor Während man mit dem For-ViewHelper durch das Objekt oder Array in der Reihenfolge iteriert, dass man das erste Element zuerst und das letzte Elemente zuletzt bekommt, kann man mit dem groupedFor-ViewHelper nach einem der Schlüssel vorsortieren. Listing 17.36 ViewHelper groupedFor // Verwendung {fruit.name} - {fruit.color}
    // Ausgabe apple - green


    406

    17.4 ViewHelper-Übersicht cherry - red
    strawberry - red
    banana - yellow


    Hier wird also das über der GroupedFor-ViewHelper eingebrachte Array nach der Farbe (groupBy="color") vorsortiert und erst dann an den For-ViewHelper übergeben. Somit erhalten wir eine nach der Farbe sortierte Ausgabe. Tabelle 17.25 Argumente für den ViewHelper groupedFor Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    as

    string

    ja



    Der Name der IterationsVariablen

    each

    array

    ja



    Das Array oder das SplObjectStorage, über das iteriert wird

    groupBy

    string

    ja



    Nach dieser hier angegebenen Eigenschaft wird gruppiert.

    groupKey

    string

    nein

    groupKey

    Der Name der Variablen, in der die aktuelle Gruppierung gespeichert wird

    17.4.12 if Zusammen mit dem Then- und Else-ViewHelper können Sie damit eine komplette IfThen-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 diese positiv war. Sollte die Bedingung negativ ausgefallen sein, wird direkt im Else-Zweig weitergemacht. Listing 17.37 ViewHelper if, then und else // Verwendung

    Es sind Posts vorhanden!!

    Keine Posts vorhanden!!



    Die Ausgabe des obigen Codebeispiels ist nun abhängig davon, ob der im Argument condition angegebene Blog Posts besitzt. Ist dies der Fall, wird die Zeichenkette Es sind Posts vorhanden!! ausgegeben. Ansonsten erfolgt die Ausgabe der Zeichenkette im ElseZweig.

    407

    17 Die Fluid-Template-Engine Tabelle 17.26 Argumente für den ViewHelper if Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    condition

    boolean

    ja



    Dies ist eine ViewHelperCondition wie in Kapitel 17.3 beschrieben.

    17.4.13 image Über den ViewHelper image können Sie Bilder in das Template laden und einige Attribute an diese übergeben, wie beispielsweise die minimale oder maximale Breite oder Höhe sowie die Angabe, ob das Bild eine Image-Map beinhalten soll. Listing 17.38 ViewHelper image // Verwendung

    Das Bild wird nun an der über src angegebenen Stelle gesucht und mittels der anderen Parameter über die TYPO3-Verarbeitung geschickt. Dabei wird das Bild dann sowohl skaliert (wenn dies notwendig sein sollte) als auch mit einer Image-Map versehen (wenn dies spezifiziert wurde). Anschließend wird das Bild ausgegeben. Tabelle 17.27 Argumente für den ViewHelper image

    408

    Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accessKey

    string

    nein



    Keyboard-Shortcut

    alt

    string

    ja



    Alt-Angabe

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    height

    string

    nein



    Höhe des Bildes – entweder als fixer Wert oder als Kalkulation mittels „m“ oder „c“

    id

    string

    nein



    ID des Bildes

    ismap

    strin

    nein



    Spezifiziert das Bild als serverseitige Image-Map (kaum benutzt)

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    longdesc

    string

    nein



    Legt eine URL fest, unter der man eine ausführliche Beschreibung für das Bild hinterlegen kann

    maxHeight

    integer

    nein



    Maximale Höhe

    maxWidth

    integer

    nein



    Maximale Breite

    minHeight

    integer

    nein



    Minimale Höhe

    17.4 ViewHelper-Übersicht Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    minWidth

    integer

    nein

    src

    string

    ja



    Quelle des Bildes

    style

    string

    nein



    zusätzliche Stilangaben

    tabIndex

    string

    nein



    TabIndex-Angabe

    title

    string

    nein



    Title-Attribut (Tooltipp)

    usemap

    string

    nein



    Spezifiziert das Bild als clientseitige Image-Map

    width

    string

    nein



    Breite des Bildes – entweder als fixer Wert oder als Kalkulation mittels „m“ oder „c“

    Minimale Breite

    17.4.14 layout Komplexe Layouts lassen sich in Fluid über eine Kombination des Layout-ViewHelpers und des Render-ViewHelpers erreichen. Näheres zu dieser Thematik finden Sie in Kapitel 17.7. Listing 17.39 ViewHelper layout // Verwendung

    Damit wird ein Template geladen, das sich im Verzeichnis Resources/Private/ Layouts/ befindet und den unter name angegebenen Namen mit der Extension .html

    trägt. Wird kein Argument für name angegeben, so wird standardmäßig der Name default verwendet. Tabelle 17.28 Argumente für den ViewHelper layout Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    name

    string

    nein

    default

    Die zu ladende Layout-Template-Datei

    17.4.15 link Über diese ViewHelper-Familie können bequem Links mit zusätzlichen Funktionalitäten angelegt werden. 17.4.15.1 link.action Dieser ViewHelper wird zur Erzeugung von Extbase-Action-Links verwendet.

    409

    17 Die Fluid-Template-Engine Listing 17.40 ViewHelper link.action // Verwendung Link auf die Show-Action des Start-Controllers // Ausgabe Link auf die Show-Action des Start-Controllers

    Tabelle 17.29 Argumente für den ViewHelper link.action

    410

    Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accessKey

    string

    nein



    Keyboard-Shortcut

    action

    string

    ja



    Die Action, die angesprochen werden soll

    additionalAttributes

    array

    nein



    Zusätzliche Parameter, die dem HTML zugefügt werden

    additionalParams

    array

    nein



    Wie additionalAttributes, nur dass die Parameter kein Präfix erhalten, sondern unverändert hinzugefügt werden

    arguments

    array

    nein



    Argumente, die an die Action übergeben werden

    class

    string

    nein



    CSS-Klasse(n)

    controller

    string

    nein

    Der aktuelle Controller

    Name des Controllers, der angesprungen werden soll

    dir

    string

    nein

    Ltr

    Textrichtung: ltr oder rtl

    extensionName

    string

    nein



    Name der Extension

    id

    string

    nein



    ID des Links

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    linkAccessRestrictedPages

    boolean

    nein



    Der Link wird se bst dann angezeigt, wenn die Zielseite aus Gründen der Zugangsberechtigung nicht angesprungen werden kann.

    noCache

    boolean

    nein



    Deaktiviert den Cache der Zielseite

    17.4 ViewHelper-Übersicht Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    noCacheHash

    boolean

    nein



    Verhindert die Erzeugung des cHashParameters im Link, der von der typolinkFunktion erzeugt wird

    pageType

    integer

    nein



    Der gewünschte typenum der Seite

    pageUid

    integer

    nein

    Die aktuelle pageUid

    Die UID der Seite, die angesprungen werden soll

    pluginName

    string

    nein



    Name des Plug-ins

    rel

    string

    nein



    Spezifiziert die Verbindung zwischen dem aktuellen und dem verlinkten Dokument

    section

    string

    nein



    Fügt eine Ankerangabe an den Link hinzu (#section)

    style

    string

    nein



    Zusätzliche Stilangaben

    tabIndex

    integer

    nein



    TabIndex-Angabe

    target

    string

    nein



    Sprungziel

    title

    string

    nein



    Title-Attribut (Tooltipp)

    17.4.15.2 link.email Hierüber können Sie E-Mail-Links erzeugen. Insbesondere werden die TYPO3-eigenen Verfahren zur Spam-Vermeidung verwendet, wenn diese entsprechend konfiguriert wurden. Listing 17.41 ViewHelper link.email // Verwendung Email an Patrick Lobacher schreiben // Ausgabe Email an Patrick Lobacher schreiben

    Tabelle 17.30 Argumente für den ViewHelper link.email Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    additionalParams

    array

    nein



    Zusätzliche Parameter, die dem HTML zugefügt werden

    email

    string

    ja



    Em-Mail-Adresse

    411

    17 Die Fluid-Template-Engine 17.4.15.3 link.external Dieser ViewHelper wird zur Erzeugung von externen Links verwendet. Listing 17.42 ViewHelper link.external // Verwendung Homepage typofaktum // Ausgabe Homepage typofaktum

    Tabelle 17.31 Argumente für den ViewHelper link.external

    412

    Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accessKey

    string

    nein



    Keyboard-Shortcut

    additionalAttributes

    array

    nein



    Zusätzliche Parameter, die dem HTML zugefügt werden

    additionalParams

    array

    nein



    Wie additionalAttr butes, nur dass die Parameter kein Präfix erhalten, sondern unverändert hinzugefügt werden

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    id

    string

    nein



    ID des Links

    lang

    string

    nein



    Language-Attr but (RFC 1766)

    linkAccessRestrictedPages

    boolean

    nein



    Der Link wird selbst dann angezeigt, wenn die Zielseite aus Gründen der Zugangsberechtigung nicht angesprungen werden kann.

    rel

    string

    nein



    Spezifiziert die Verbindung zwischen dem aktuellen und dem verlinkten Dokument

    style

    string

    nein



    zusätzliche Stilangaben

    tabIndex

    integer

    nein



    TabIndex-Angabe

    target

    string

    nein



    Sprungziel

    title

    string

    nein



    Title-Attribut (Tooltipp)

    uri

    string

    ja



    Die URL, zu der verlinkt werden soll

    17.4 ViewHelper-Übersicht 17.4.15.4 link.page Dieser ViewHelper wird Erzeugung von internen Links verwendet. Listing 17.43 ViewHelper link.page // Verwendung Link auf die Seite mit der UID=1 // Ausgabe Link auf die Seite mit der UID=1

    Tabelle 17.32 Argumente für den ViewHelper link.external Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    accessKey

    string

    nein



    Keyboard-Shortcut

    additionalAttributes

    array

    nein



    Zusätzliche Parameter, die dem HTML zugefügt werden

    class

    string

    nein



    CSS-Klasse(n)

    dir

    string

    nein

    ltr

    Textrichtung: ltr oder rtl

    id

    string

    nein



    ID des Links

    lang

    string

    nein



    Language-Attribut (RFC 1766)

    noCache

    boolean

    nein



    Deaktiviert den Cache der Zielseite

    noCacheHash

    boolean

    nein



    Verhindert die Erzeugung des cHash-Parameters im Link, der von der typolink-Funktion erzeugt wird.

    pageType

    integer

    nein



    Der gewünschte typenum der Seite

    pageUid

    integer

    nein

    Die aktuelle pageUid

    Die UID der Seite, die angesprungen werden soll

    rel

    string

    nein



    Spezifiziert die Verbindung zwischen dem aktuellen und dem verlinkten Dokument

    section

    string

    nein



    Fügt eine Ankerangabe an den Link hinzu (#section)

    style

    string

    nein



    zusätzliche Stilangaben

    tabIndex

    integer

    nein



    TabIndex-Angabe

    target

    string

    nein



    Sprungziel

    title

    string

    nein



    Title-Attr but (Tooltipp)

    uri

    string

    ja



    Die URL, zu der verlinkt werden soll

    413

    17 Die Fluid-Template-Engine

    17.4.16 render Dieser ViewHelper dient dazu, Partials oder Sections zu rendern. Wird der ViewHelper in einem Layout-Template-File verwendet, so wird der Content im Haupt-Template-File gerendert. Wird der ViewHelper allerdings schon in einem Haupt-Template-File verwendet, so wird der jeweilige Sections-Abschnitt im selben Template gerendert. Nähere Informationen hierzu finden Sie in Kapitel 17.7. Wird allerdings der Parameter partial gesetzt, so wird der Wert mit .html ergänzt und ein damit gleichnamiges Template im Verzeichnis Resources/Private/Partials geladen und gerendert. Listing 17.44 ViewHelper render // Anwendung // Anwendung

    Tabelle 17.33 Argumente für den ViewHelper render Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    arguments

    array

    nein



    Ein Array mit Argumenten, die man an den Renderer weitergeben möchte

    partial

    string

    nein



    Angabe des Partials, das gerendert werden soll

    section

    string

    nein



    Angabe der Section, die gerendert werden soll

    17.4.17 renderFlashMessages Mit der Methode $this->flashMessages->add() können sogenannte Flash-Messages zugefügt werden. Diese beinhalten meist Statusinformationen, wie „Das Blog wurde zugefügt“, „Der Post wurde erfolgreich gelöscht“ oder „Ihre letzte Transaktion hat 7 Elemente gelöscht“ und Ähnliches. Mit diesem ViewHelper können die vorher zugefügten FlashMessages zugefügt werden. Listing 17.45 ViewHelper renderFlashMessages // Verwendung

    Die Ausgabe erfolgt per Default als ungeordnete Liste, wobei jede vorher zugefügte FlashMessage ein Listenelement darstellt. Will man eine andere Darstellung erreichen, so muss man dafür einen eigenen ViewHelper schreiben.

    414

    17.4 ViewHelper-Übersicht Tabelle 17.34 Argumente für den ViewHelper renderFlashMessages Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    class

    string

    nein



    Fügt dem Listenelement eine CSS-Klasse hinzu

    17.4.18 section Über diesen ViewHelper können Sie in einem Template einen Bereich definieren, der durch ein Layout gerendert wird. Weitere Informationen hierzu finden Sie in Kapitel 17.7. Listing 17.46 ViewHelper section // Verwendung … … Inhalt …

    Durch den Render-ViewHelper wird die mit diesem ViewHelper definierte Sektion dann gerendert. Tabelle 17.35 Argumente für den ViewHelper render Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    name

    string

    ja



    Name der Sektion

    17.4.19 then Der Then-ViewHelper macht nur in Zusammenhang mit dem If-ViewHelper Sinn und wird dementsprechend dort behandelt.

    17.4.20 translate Über diesen ViewHelper können mehrsprachige Labels verwendet werden. Dafür wird 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/ ermittelt. Dort wird über den Parameter key der entsprechende Schlüssel ausgelesen und dessen Wert zurückgegeben. Listing 17.47 Der ViewHelper translate // Aufbau locallang.xml <meta type="array">

    415

    17 Die Fluid-Template-Engine module <description>Sprachlabels für die Efempty Extension
    // Verwendung // Ausgabe (bei Konfiguration der deutschen Sprache) Das ist die deutsche Ausgabe

    Tabelle 17.36 Argumente für den ViewHandler translate Name

    Typ

    benötigt

    Default-Wert

    Bemerkung

    htmlEscape

    boolean

    nein



    Ist der Wert TRUE, so wird die Ausgabe HTML escaped.

    key

    string

    ja



    Der Schlüssel, über den der Wert in der locallang-Datei ermittelt wird

    17.4.21 uri Die ViewHelper-Familie uri ist identisch zu der Link-Familie – mit der Ausnahme, dass lediglich der Link selbst zurückgegeben wird (sprich ohne -Tag) – so gibt es also uri.action, uri.email, uri.external und uri.page.

    17.5

    Erstellen eines eigenen ViewHelpers Nachdem wir nun die mitgelieferten ViewHelper genauer inspiziert haben, wollen wir uns jetzt an die Arbeit machen, selbst einen ViewHelper zu schreiben. Genau dies macht Fluid so mächtig – wir können den Kern von Fluid nämlich auf diese Weise einfach und bequem selbst erweitern. Grundsätzlich müssen für einen eigenen ViewHelper folgende Schritte ausgeführt werden:

    „ Anlegen eines Verzeichnisses Classes/ViewHelpers in Ihrer Extension „ Ausdenken eines Namens für den ViewHelper „ Anlegen eine Klassendatei NameViewHelper.php im oben angelegten Verzeichnis

    416

    17.5 Erstellen eines eigenen ViewHelpers

    „ In der Klassendatei platzieren Sie eine Klasse mit dem Namen: Tx_ExtensionName_ ViewHelpers_NameViewHelper,

    die AbstractViewHelper abgeleitet wird.

    von

    der

    Klasse

    Tx_Fluid_Core_

    „ Hinzufügen einer Funktion render() Das war es im Grunde genommen schon – so einfach ist es prinzipiell, einen ViewHelper zuzufügen. Wir wollen dies im Folgenden an ein paar unterschiedlichen Beispielen zeigen.

    17.5.1

    Der Dummytext-ViewHelper

    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, könnte man doch schon mal Dummy-Texte dort platzieren. Und genau dies soll der Dummytext-ViewHelper erledigen. Zunächst benötigen wir also eine Datei DummytextViewHelper.php im Verzeichnis Classes/ViewHelpers/, das wir im Verzeichnis der vorhin schon benutzten Extension efempty aus dem TER anlegen. Listing 17.48 Die Datei DummytextViewHelper.php

    In diesem ViewHelper wird nun Folgendes durchgeführt:

    „ Offensichtlich gibt es einen Parameter length, der – wenn er denn nicht übermittelt wurde – per Default die Länge 100 enthält.

    „ Nun wird der angegebene Dummytext-String so oft wiederholt, bis die angegebene Anzahl an Zeichen erreicht wurde. Anschließend wird der String wieder zurückgegeben.

    417

    17 Die Fluid-Template-Engine

    „ Nun ist es aber noch etwas ungeschickt, dass wir den Dummytext fest in den Code geschrieben haben. Wenn wir beispielsweise nun einen anderen Text haben wollten, so funktioniert dies nur für alle Texte gleichermaßen, indem wir den Code ändern.

    „ Wir könnten aber den Dummy-Text jeweils beim Verwenden des ViewHelpers notieren, dann würde dieser Text nur für diesen speziellen ViewHelper gelten und damit individuell anpassbar sein.

    „ Wir verändern also den Aufruf unseres ViewHelpers im Template: Listing 17.49 Anpassen des ViewHelpers um einen individuellen Dummy-Text im Template {namespace efempty = Tx_Efempty_ViewHelpers} <efempty:dummytext length="60">Das ist ein Dummytext!

    Wir haben nun also den Dummy-Text zwischen die Tags geschrieben. Um nun auf diesen zuzugreifen, müssen wir diesen entsprechend ansprechen. Die dafür notwendige Funktion lautet $this->renderChildren(). Damit wird der komplette Inhalt des ViewHelpers zunächst einmal gerendert. Die Ausgabe dann wird dem umgreifenden ViewHelper zur Verfügung gestellt. Dafür müssen wir die render()-Funktion unseres ViewHelpers anpassen. Listing 17.50 Anpassung an die render()-Funktion … public function render($length = 100) { //$dummytext = 'Lorem ipsum dolor sit amet. '; $dummytext = $this->renderChildren(); $len = strlen($dummytext); …

    Nun könnten wir aber auch anstelle eines Textes einen anderen ViewHelper als Quelle für unseren Dummy-Text verwenden – beispielsweise einen Translate-ViewHelper. Schon bekommen wir Dummy-Texte, die von der eingestellten Sprache abhängig sind: Listing 17.51 Verwenden des Translate-ViewHelpers als Quelle für den Dummy-Text // Verwendung {namespace efempty = Tx_Efempty_ViewHelpers} <efempty:dummytext length="60">Dummytext: // Ausgabe Dummytext: Das ist die deutsche AusgabeDummytext: Das ist di

    17.5.2

    Zugriff auf die übergebenen Argumente

    Innerhalb des ViewHelpers haben Sie (wenn auch nur lesenden) Zugriff auf die übergebenden Argumente mittels $this->arguments:

    418

    17.5 Erstellen eines eigenen ViewHelpers Listing 17.52 Zugriff auf die an einen ViewHelper übergebenden Argumente // Verwendung debug($this->arguments) // Ausgabe Tx_Fluid_Core_ViewHelper_Arguments Object ( [arguments:protected] => Array ( [length] => 60 ) )

    17.5.3

    Zufügen von Argumenten

    Das Zufügen von Argumenten kann prinzipiell auf zwei Wegen geschehen:

    „ Wie in Kapitel 17.5.1 gezeigt, kann man die Argumente einfach als MethodenArgumente an die Methode render() übergeben und über die Annotation validieren lassen (also den Typ überprüfen).

    „ Ein

    weiterer Weg ist, explizit die folgende Funktion aufzurufen: $this->registerArgument($name, $dataType, $description, $isRequired, $defaultValue=NULL)

    ƒ

    $name

    Der Name des Arguments

    ƒ

    $dataType

    Der Datentyp des Arguments

    ƒ

    $description

    Eine kurze Beschreibung

    ƒ

    $isRequired

    Angabe, ob dieses Argument unbedingt notwendig ist (Wert = 1) oder auch weggelassen werden kann (Wert = 0)

    ƒ

    $defaultValue

    Die Angabe eines Default-Wertes

    17.5.4

    Tag-basierende ViewHelper

    Viele ViewHelper geben in irgendeiner Weise am Ende ein HTML-Tag aus. Dieses ist meist mit zahlreichen Attributen versehen. Diese können natürlich auch selbst „händisch“ per String-Konkatenation (a là if ($this->arguments['class']) { $output .= ' class="' . $this->arguments['class'] . '"'; }) hinzugefügt werden, allerdings hilft uns Fluid mit speziell hierfür vorgesehenen Methoden und Eigenschaften:

    „

    protected $tagName = 'a'

    ƒ Mit dieser Eigenschaft wird der Name des späteren Tags festgelegt – in diesem Beispiel also wird ein
    -Tag zusammengestellt.

    419

    17 Die Fluid-Template-Engine

    „

    $this->registerUniversalTagAttributes()

    ƒ Dies registriert die folgenden Attribute zur Verwendung mit diesem Tag: class, dir, id, lang, style, title, accesskey, tabindex

    „

    $this->registerTagAttribute($name, $type, $description, $required)

    ƒ Mit dieser Methode kann ein eigenes Attribut registriert werden, das dann – sofern es verwendet wird – automatisch mit gerendert wird.

    ƒ

    $name

    Der Name des Attributs

    ƒ

    $type

    Der Datentyp des Attributs

    ƒ

    $description

    Eine kurze Beschreibung

    ƒ

    $required

    Die Angabe, ob dieses Attribut angegeben werden muss

    „

    $this->tag->addAttribute($name, $type, $description, $required)

    ƒ Mit dieser Methode können Sie Attribute angeben, die dem Tag direkt zugefügt werden sollen

    „

    $this->tag->setContent($taginhalt)

    ƒ Hiermit wird der Inhalt innerhalb der Tags gesetzt. Dieser kann beispielsweise über $this->renderChildren() ermittelt werden.

    „

    $this->tag->render()

    ƒ Rendert das Tag schließlich 17.5.5

    Der Variablen-Container

    Wenn man einen ViewHelper hat, der sämtliche benötigte Daten aus der Eingabe nehmen kann, dann sind Sie hier bereits fertig mit Ihrem ViewHelper. Haben Sie jedoch beispielsweise eine Schleife, die in jedem Schleifendurchlauf einen eigenen Wert verarbeitet, dann müssen Sie leicht anders vorgehen. Diese Problematik wird mit einem Variablen-Container gelöst. Dieser nimmt eine Variable auf, daraufhin wird der Inhalt mit eben diesem Wert gerendert, anschließend wird die Variable wieder entfernt, und der nächste Schleifendurchlauf beginnt. Nachfolgend der Code eines Loop-ViewHelpers, der letztlich nichts anderes macht, als eine foreach()-Schleife nachzuahmen. Dieser ViewHelper iteriert einfach durch ein vorgegebenes Array. Listing 17.53 Ein eigener foreach-ViewHelper mit dem Name loop
    420

    17.6 Verwendung von Fluid in klassischen Extensions

    /** * Renders a loop * * @param array $each Das Array, über das iteriert wird * @param string $as Die Iterations-Variable */ public function render(array $each, $as) { $out = ''; foreach ($each as $singleElement) { $this->templateVariableContainer->add($as, $singleElement); $out .= $this->renderChildren(); $this->templateVariableContainer->remove($as); } return $out; } } ?>

    Listing 17.54 Die Verwendung des Loop-ViewHelpers im Template // Verwendung {namespace efempty = Tx_Efempty_ViewHelpers} <efempty:loop each="{0:'test', 1:2, 2:3, 3:4, 4:5}" as="value"> Der Wert heisst: {value}
    // Ausgabe Der Wert heisst: Der Wert heisst: Der Wert heisst: Der Wert heisst: Der Wert heisst:

    test
    2
    3
    4
    5


    Die Vorgehensweise ist dabei wie folgt:

    „ Über

    $this->templateVariableContainer->add($varname, $value) wird ein

    Wert registriert

    „ Mittels $out

    .= $this->renderChildren(); wird das Template mit der eben zugefügten Variablen gerendert (und der gerenderte Inhalt an die Ausgabevariable – hier $out – zugefügt).

    „ Am

    Variable mittels >templateVariableContainer->remove($varname); wieder entfernt.

    17.6

    Ende

    (des

    Durchlaufs)

    wird

    dann

    die

    $this-

    Verwendung von Fluid in klassischen Extensions So bequem die Verwendung von Fluid in Extensions ist, die auf Extbase basieren, so praktisch wäre es doch manchmal auch, Fluid genauso in klassischen Extensions verwenden zu können. Einerseits will man vielleicht noch nicht die komplette Extension umwandeln, oder es ist andererseits schlicht noch nicht möglich, die Extension komplett mit Extbase zu realisieren – vielleicht weil diese mit anderen Extensions interagiert oder weil Features fehlen, die zur Realisierung unbedingt benötigt werden.

    421

    17 Die Fluid-Template-Engine Daher können Sie Fluid natürlich auch in klassischen Extensions ohne großen Aufwand einsetzen. Zum Test haben wir mit dem Kickstarter eine Demo-Extension mit dem Namen fluidohneextbase (Fluid ohne Extbase) erstellt, die lediglich ein Plug-in enthält – sonst nichts. In die Plug-in-Klassendatei class.tx_fluidohneextbase_pi1.php im Verzeichnis typo3conf/ext/fluidohneextbase/pi1/ schreiben wir nun in die Funktion main() folgenden PHP-Code (der neue Code ist hervorgehoben): Listing 17.55 PHP-Code zum Ansprechen der Fluid-Funktionalität conf = $conf; $this->pi_setPiVarDefaults(); $this->pi_loadLL(); // Fluid-Rendering festlegen $renderer = t3lib_div::makeInstance('Tx_Fluid_View_TemplateView'); // Legt den Controller-Context fest $renderer>setControllerContext(t3lib_div::makeInstance('Tx_Extbase_MVC_Controller_ControllerContext ')); // Pfade des Fluid-Templates festlegen $template = t3lib_extMgm::extPath($_EXTKEY) . 'res/index.html'; $renderer->setTemplatePathAndFilename($template); // Normale Fluid-Zuweisung von Objekten, String, … an das Template $renderer->assign('helloworld', 'Hello World!'); // Rendern des Templates $content .= $renderer->render(); return $this->pi_wrapInBaseClass($content); } } … ?>

    Wichtig ist lediglich, dass sowohl die Extension fluid wie auch die Extension Extbase installiert ist – Letztere wird lediglich dafür gebraucht, um den Rendering-Context festzulegen. Die selbst geschriebene Extension greift aber ansonsten nicht auf Extbase zu – lediglich Fluid.

    422

    17.7 Layouts und Partials Somit sind Sie in der Lage, Ihre eigenen, klassischen Extensions ebenfalls mit der mächtigen und flexiblen Template-Engine Fluid anzureichern.

    17.7

    Layouts und Partials Gerade wenn man eine umfangreiche Webapplikation entwirft, stellt man fest, dass man im Grunde genommen drei wiederkehrende Bestandteile in der Ausgabe hat.

    „ Das Layout, welches das CI enthält (also Logos, Menüleiste, Suche …) „ Ein Template, das die gerade benötigte Action (beispielsweise eine Liste, ein Formular oder eine Einzelansicht) darstellt

    „ Immer wiederkehrende Teile (Partials), die beispielsweise Fehlermeldungen anzeigen können. Sobald ein bestimmter Abschnitt im Template öfter als einmal unverändert auftaucht, kann es schon Sinn machen, diesen in ein Partial auszulagern

    Abbildung 17.3 Grafische Darstellung von Layout, Template und Partial

    „ Um nun diese Struktur zu realisieren, müssen Sie zunächst unterhalb von „ Resources/Private/ folgende Verzeichnisse anlegen: „ Layouts „ Partials

    423

    17 Die Fluid-Template-Engine

    Abbildung 17.4 Verzeichnisstruktur im Private-Ordner zur Realisierung von Layouts und Partials

    Nun starten wir mit dem Haupt-Template index.html: Listing 17.56 Das Template index.html

    It works :-)

    Weiterer Text…


    Beim Aufbau der Layout-Template-Struktur muss man nun etwas um die Ecke denken: 1. Zuerst wird über die Action das Template-File im zum Model passenden Unterverzeichnis von Templates definiert. 2. Dieses Template-File kann nun einen Layout-ViewHelper beinhalten (oben in der ersten Zeile), der wiederum bestimmt das Layout-Template-File. An diesen unter dem Attribut name angegebenen Namen wird nun .html zugefügt und das File aus dem Verzeichnis Resources/Private/Layouts geladen. 3. Im Layout-Template-File nun können Sie das Layout definieren. An der Stelle, an der Sie das Template platzieren wollen, verwenden Sie den ViewHelper . Als Attribut section nehmen Sie genau denjenigen Namen, den Sie im Haupt-Template über den Section-ViewHelper verwendet haben. Damit wird dann der Abschnitt (Section) des Templates gerendert. 4. Partials können Sie nun beliebig platzieren, indem Sie den Render-ViewHelper verwenden.

    424

    17.7 Layouts und Partials Listing 17.57 Das Layout-File defaultLayout.html

    Welcome to the efempty extension!



    © typofaktum - Patrick Lobacher



    Über diesen Mechanismus ist es möglich, auch komplexe Layouts zu realisieren. Allerdings ist es momentan noch nicht möglich, Fluid als Output-Template-Engine für TYPO3 selbst zu verwenden. Hier muss (vorläufig) weiterhin mit Marker/Subparts bzw. TemplaVoilà gearbeitet werden. Lediglich die Ausgabe der Extension selbst ist mittels Fluid steuerbar.

    425

    Register $

    _GET 165 _GETset 165 _GP 166 _POST 166

    addModulePath() 66 addSlashesOnArray 186 addTCAcollumns() 54 Aggregate 285 AJAX 114, 205, 257 AJAX-Dispatcher 258 AJAX-Request 207 all 232 anchorPrefix 233 applicationData 233 Applikationsschicht 281 Arbeitsfläche 201 array_merge 187 array_merge_recursive_overrule 187 array2json 188 array2xml 188 arrayToLogString 189 ATagParams 233 Attribute 268 Ausgabe 103

    A

    B

    Abmessungen 201 absRefPrefix 232 abstract 274 abstrakte Klasse 273 Action anlegen 335 additionalCSS 228 additionalHeaderData 224, 228, 232 additionalJavaScript 224 addModule() 66

    Backend User Object 91 Backend-Flexform 300 Backend-Formulare 34, 82 Backend-Modul 40 Backend-Modul (Extbase) 365 baseUrl 234 baseUrlWrap 215, 234 Basisdatei 206 Bearbeitungssymbole 84

    $BE_USER 91 $EM_CONF[$_EXTKEY] 46 $extpath 67 $GLOBALS 82 $GLOBALS['TSFE']->fe_user->user 245 $GLOBALS['TYPO3_DB'] 141 $MCONF 50 $TCA 53 $temp_TBE_MODULES 67 $this->content 79

    [ [Passthrough] 36

    _

    427

    Register BEER3 380 Berechtigungsprüfung 75 Berlin Manifesto 266 Berliner Transition Days 379 beUserLogin 234 bigDoc 76 Bilder 101, 104, 199 Bildupload 60 Blog 26 Blog-Example 287

    C cacheExpires 209 calcAge 195 calcParenthesis 171 callUserFunction 166 CGL 9 Changelog 22 Checkbox single 35 Checkbox, 4 Boxes in a Row und 10 Boxes in two Rows 35 Checkboxen 57 checkEmail 195 cImage 211 class.tx_extkey_pi#.php 52 cleanFormName 211 cleanIntArray 147 cleanIntList 147 clearPageCacheContent 209 clearPageCacheContent_pidList 209 cli 259 Clickmenu items 41 clientInfo 167, 234 cliKey 259 cmpFQDN 171 cmpIP 172 cObj 151 cObjGet 211 cObjGetSingle 212 Codeformatierung 11 codeString 195 compat_version 167 compileSelectedGetVarsFromArray 168

    428

    Conditions 17, 91 conf.php 50 content 235 Content-Element 39 Controller 281 Convention over Configuration 294 convUmlauts 172 Copyright 12 css_styled_content 96 CSS-Datei 86 csvValues 172 ctrl-Bereich 54 currentPageUrl 212

    D Database Relation 36 Date und Date Time 35 Dateistruktur 12 Dateisystem 155 Daten persistieren 324 Datenbank 28 Datenbank-Abstraktions-Layer 141 Datenbankfelder 29 Datenbank-Handler 141 Datenbankstruktur 25 Datenbanktabellen 34 DBAL 141 DBgetDelete 151 DBgetInsert 151 DBgetUpdate 152 debug 260 Debug 17 debug_lastBuiltQuery 147 debug_ordvalue 261 debug_trail 261 Debugging 260 debugRows 261 defaultBodyTag 235 defVals 82 deHSCentities 173 Destruktor 271 devLog 262 Dienste 42

    Register dirname 155 Dispatcher 306 Dokumentation 133 Dokumententypen 257 Domain Driven Design 280, 291 Aggregate 285 Entity 285 Factory 286 Repository 286 Service 285 Ubiquitous Language 282 Value Object 285 domainStartPage 235 Domänenschicht 281

    E efempty Extension 380 eID 114, 205 enableFields 153 encryptEmail 196 Entitäten einführen 322 Entity 285 escapeStrForLike 146 exec_DELETEquery 143 exec_INSERTquery 143 exec_SELECT_mm_query 142 exec_SELECT_queryArray 141 exec_SELECTgetRows 142 exec_SELECTquery 141 exec_UPDATEquery 143 expandList 173 explodeUrl2Array 190 ext_conf_template.txt 80, 244 ext_emconf.php 27, 45, 296 ext_icon.gif 302 ext_localconf.php 53, 205, 301 ext_tables.php 53, 66, 297 ext_tables.sql 54 ext_tables_static+adt.sql 55 Extbase 266 Extbase-Kickstarter 366 extdeveval 14, 133 Extend existing Tables 36

    extends 271 Extension-ID 205 Extension-Key 8, 315 Extension-Manager 244 ExtJS 119 extTarget 236

    F Factory 286 Features 24 Fehlerausgabe 347 Feldtyp 81 fePreview 236 Field name 29 fileResource 213 Files 36 finale Klasse 273 fixed_lgd 173 fixed_lgd_pre 174 fixWindowsFilePath 155 FlashMessages 377 Flex 36 Flexform 125, 242, 249 FLOW3 265 Fluid 266, 379 Argumente 385 Arrays 382 Basissyntax 382 Layout 423 Namespace 384 Objekte 383 Partial 423 Verwendung in klassischen Extensions 421 ViewHelper-Syntax 384 formatForTextarea 174 Formatierung 11 formatSize 156 Formulardaten 243 Formulardaten eingeben 337 Formulare 34, 251 Formularfelder 253 Frontend-Bibliotheken 4 Frontend-Plug-ins 37

    429

    Register Frontend-Rendering-Prozess 4, 205 Frontend-User 245 fullQuoteArray 146 fullQuoteStr 145

    G General Info 27 generateRandomBytes 174 get_cache_timeout 210 get_dirs 156 get_tag_attributes 175 getAllFilesAndFoldersInPath 157 getBrowsableTree() 72 getFileAbsFileName 158 getFilesInDir 158 getHostname 168 getImgResource 213 getIndpEnv 168 getLLL 230 getMailTo 215 getSlidePids 213 getThisUrl 168 getTypoLink 216 getTypoLink_URL 216 getURL 159 GIFBUILDER 104 gifBuilderTextBox 214 Globale Extensions 7 GPL 12 GraphicsMagick 200 Graustufen 202

    H Hauptmodul 65 header type 39 Hook 123 HTMLcaseshift 196 htmlspecialchars_decode 175 HTML-Template 109 HTML-Templates 103 http_makelinks 217

    430

    I Icon 83 id 236 imageLinkWrap 217 ImageMagick 200 imagesOnPage 237 Implementierung 269 implodeArrayForUrl 175 implodeAttributes 176 inArray 190 Infrastrukturschicht 281 Ingmar Schlecht 366 initialisieren 75 inList 176 Input-Felder 58 Installation von Extbase 287 Installation von Fluid 287 instanceof-Operator 272 Instanziieren 268 int_from_ver 177 Integer, 10 -1 000 35 Integrate in existing Modules 41 Interface 275 intInRange 177 intTarget 237 intval_positive 178 isAbsPath 159 isAllowedAbsPath 159 isFirstPartOfStr 178

    J JavaScript 115, 206 Jochen Rau 266 JSeventFuncCalls 225 JSON 122

    K Kapselung 274 Kategorie 80 keywords 196 Kickstarter 26 Klassen 267 Klassenzugehörigkeit 272

    Register Kommentare 16 Konstanten 43 Konstruktor 269 Konzept 23

    L lang 237 lastImageInfo 238 linebreaks 197 Link 35 linkThisScript 169 linkThisUrl 169 Liste 85 Listenansicht 102 Listenausgabe 99 listQuery 144 listView 98 locallang.xml 51, 56 locallang_db xml 56, 250 locallang_mod xml 51 locationHeaderUrl 169 loginUser 238 Lokale Extensions 7

    modifyHTMLColor 179 modifyHTMLColorAll 179 Modul 40 Modulinhalt 79 MVC (Model-View-Controller) 281 MVC-Request Objekt 376

    N Namenskonventionen 293 Namespace 10, 278 Navigationsbaum 65, 69 new-Operator 269 no_cache 238 noDoc 76 normalizeIPv6 180 Not editable, only displayed 36

    O Objekte 267 objektorientierte Programmierung 267 OpenOffice 135 Optionale Argumente 347

    P M mailto_makelinks 218 mailto-Link 106 makeInstance 170 makeInstanceService 170 Marker 109 md5int 178 mediumDoc 76 Mehrsprachigkeit 250, 361 Methoden 268 Methodendeklaration 268 Methodenrumpf 269 milliseconds 179 minifyJavaScript 225 mkdir 160 mkdir_deep 160 M-M-Relationen 62 Model 281 Modellierung 282

    page 239 Page-Browser 87 phpDoc 12, 21 PHP-Shell 259 PHP-Syntax 15 PHP-Tags 11 PHPTAL 379 pi_exec_query 148 pi_getClassName 229 pi_getFFvalue 256 pi_getLL 230 pi_getPageLink 218 pi_getPidList 149 pi_getRecord 150 pi_linkToPage 218 pi_linkTP 219 pi_linkTP_keepPIvars 219 pi_linkTP_keepPIvars_url 219 pi_list_browseresults 221

    431

    Register pi_list_header() 101 pi_list_linkSingle 222 pi_list_makelist 222 pi_list_modeSelector 223 pi_list_searchBox 223 pi_openAtagHrefInJSwindow 220 pi_prependFieldsWithTable 150 pi_setClassStyle 229 Planung 23 Plug-in 37 Präsentationsschicht 281 prefixLocalAnchorsWithScript 220 print_array 190 printError 239 private 274 processParams 197 protected 274 public 274

    Q Query-Manager 341 quoteJSvalue 226 quoteStr 145

    R Radiobuttons 36 rawUrlEncodeJS 226 readLLfile 231 Registrierung 8 Relation 348 Relation m n 356 removeArrayEntryByValue 191 removePrefixPathFromList 160 removeXSS 180 Repository 23, 286 anlegen 333 Objekte entfernen 338 Update von Objekten 339 resolveBackPath 161 revExplode 180 rm_endcomma 181 rmdir 161

    432

    rmFromList 170 rootLine 240 RTE 105

    S Scope Resolution Operator 272 searchQuery 144 Sebastian Kurfürst 379 Seitenbaum 72 Selectorbox 35 self 278 Services 42, 259, 285 Servicetyp 43 Session-Key 246 set_cache_timeout_default 210 set_no_cache 210 setCSS 229 setJS 227 Setup 43 shortMD5 181 showRecordFieldList 56 Signatur 268 singleView 98 siteScript 240 slashArray 191 smallDoc 76 Smarty 379 split_fileref 162 split_tag_attributes 181 splitCalc 182 splitGroupOrderLimit 145 Sprachdatei 102, 250 sql_fetch_assoc 143 sql_fetch_row 144 SQL-Abfrage 78, 131 static 278 Static TypoScript code 43 Statische Eigenschaften 277 Statische Methoden 277 stdWrap 214 StdWrap 110 StoragePid 374 String input 34

    Register String input, advanced 34 strtolower 183 strtoupper 183 Sub- or Mainmodule 40 Subpart 109 substUrlsInPlainText 183 Subversion 13 SVN 13 sysLog 171 Systemextensions 7 SystemLog 92

    T t3lib_div 155 t3lib_extMgm 66 t3lib_scbase.php 75 t3lib_treeview 71 Tabellen 29 Table Configuration Array 53 Tabs 251 tca.php 56 template 76 Templating-Klasse 76 tempnam 162 TER 137 testInt 184 Text 204 Text area 34 Text area with RTE 34 Text area, no wrapping 35 Textfelder mit RTE 61 this 270 tmpl->config, tmpl->setup 241 trimExplode 184 TrueType 204 TSconfig 43 tslib_eidtools 121 tslib_pibase 96, 148 type 241 Type Hint 277 TYPO3 5 x 265 TYPO3 Coding Guidelines 9 TYPO3_CONF_VARS 241

    TYPO3-Kern 3 TypoScript 43, 244, 257 TypoScript Konfiguration 363

    U Ubiquitous Language 282 UML 282 uniqueHash 198 uniqueList 185 uniqueString 242 unlink_tempfile 162 Upload 137 upload_copy_move 162 upload_to_tempfile 163 uploads/extkey 60 UpperCamelCase 268, 294 URLqMark 198 User-TS 91

    V Validator 343 eigene Validatoren 344 vordefinierte Validatoren 343 validEmail 185 Validierung 343 Validierung mit eval 58 validIP 185 validIPv4 186 validIPv6 186 Value Object 285 Vererbung 271 verifyFilenameAgainstDenyPattern 163 Versionsnummer 44, 137 Verzeichnis Classes 303 Verzeichnis Configuration 304 Verzeichnis Module 305 Verzeichnis Resources 305 Verzeichnisstruktur 302 View 281 View hinzufügen 320 View result 44 view_array 192 ViewHelper

    433

    Register alias 387 base 388 cObject 388 count 389 cycle 389 debug 390 else 390 Erstellung eines eigenen 416 for 390 form 391 form.checkbox 393 form.error 394 form.hidden 394 form.password 395 form.radio 396 form.select 397 form.submit 399 form.textarea 400 form.textbox 401 form.upload 401 format 402 format.crop 402 format.currency 403 format.date 404 format html 404 format nl2br 405 format number 405 format.padding 405 format.printf 406 groupedFor 406 if 407 image 408

    434

    layout 409 link 409 link.action 409 link.email 411 link.external 412 link.page 413 render 414 renderFlashMessages 414 section 415 then 415 translate 415 uri 416

    W Weblog 26 wrapJS 227 writeFile 164 writeFileToTypo3tempDir 164

    X XCLASS 15 xml2array 192 xml2tree 193 xmlGetHeaderAttribs 194 XML-Response 206 XML-Struktur 126

    Z Zeichensatz 11 Zeilenumbruch 11 Zuschneiden 201

    Neues aus der TYPO3-Küche.

    Ebner/Lobacher TYPO3 und TypoScript – Kochbuch Lösungen für die TYPO3-Programmierung mit TypoScript und PHP 2., überarbeitete Auflage 864 Seiten ISBN 978-3-446-41733-5

    TYPO3 lässt von Haus aus kaum Wunsche offen. Wer aber die Möglich keiten dieses mächtigen CMS voll ausschöpfen will, muss mit dem TYPO3-System bestens vertraut sein. Hier hilft dieses Buch mit seinen zahlreichen Rezepten. Im bewährten Stile der Hanser-Kochbucher liefern die Autoren Lösungen und Lösungsvorschläge zu typischen Problemen der TYPO3- bzw. TypoScript-Programmierung. Die uber 300 Rezepte sind dabei thematisch sortiert, ihre Bandbreite reicht von der Bedienung uber die Konfiguration bis hin zu der Entwicklung eigener Erweiterungen, der Systemsicherheit und dem System-Tuning. Das Buch empfiehlt sich allen erfahrenen TYPO3-Anwendern und -Entwicklern, die nach Lösungen suchen, um das TYPO3-Basis-CMS per TypoScript und mit Hilfe von PHP an individuelle Bedurfnisse anzupassen.

    Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    Machen Sie mehr aus TYPO3!

    Koch TYPO3 und TypoScript 400 Seiten. ISBN 3-446-40751-0

    TYPO3 ist eines der populärsten Content Management Systeme. Durch seine rasche Verbreitung ist weiterführende Literatur gefragt, die zeigt, wie sich TYPO3 anpassen und erweitern lässt. Genau in diese Kategorie fällt dieses Buch. Daniel Koch zeigt Ihnen darin, wie Sie die Programmierung von Webseiten, die Erstellung von Templates und die Entwicklung von Extensions unter TYPO3 mit Hilfe von TypoScript optimieren können. Nach einer Einführung in Sprachsyntax und Funktionsweise von TypoScript stellt er weiterführende Themen vor, so u.a. Grafikbearbeitung, Menüs, Frames und Formulare sowie TypoScript und SQL.

    Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    Alby macht mobil.

    Alby Das mobile Web 240 Seiten. ISBN 978-3-446-41507-2

    Mit »Das mobile Web« knüpft Tom Alby konzeptionell an seinen Bestseller über das Web 2.0 an. Er beschreibt, wie sich typische Anwendungen der Web 2.0-Generation wie Flickr, Youtube, Newsgator, LastFM oder GMail ihren Weg auf mobile Geräte wie Handy, Smartphone, PDA oder iPod/iPhone bahnen. Neben einem ausführlichen, auch für Laien verständlichen Überblick über die Technik bestimmen vor allem die nichttechnischen Aspekte das Buch. Alby zeigt auf, welche Anwendungen im mobilen Web denkbar sind, welche bereits existieren und welchen Herausforderungen sich sowohl Hersteller als auch Anwender solcher mobilen Applikationen stellen müssen. Das Buch wendet sich an Web-User und Web-Professionals gleichermaßen, die erfahren möchten, wie sie das mobile Web optimal für sich nutzen können.

    Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    Was Sie über AJAX wissen müssen!

    Darie/Brinzarea/ Chereches¸-Tos¸a/Bucica Ajax und PHP 288 Seiten. ISBN 3-446-40920-3

    “AJAX und PHP” liefert Webprogrammierern alles, was sie brauchen, um mit AJAX in Verbindung mit PHP – der meistverbreiteten Skriptsprache weltweit - interaktive Webanwendungen zu erstellen. In der Einführung erfährt der Leser kurz und knapp die wichtigsten Grundlagen zu AJAX. Im Hauptteil dreht sich dann alles um den gelungenen Einsatz von AJAX. In sieben Kapiteln werden Anwendungsbei– spiele vorgestellt, wie sie in der Praxis geläufig sind – Form Validation, Chat, Suggest & Autocomplete, Real-time Charting, Grid Computing, RSS Reader sowie Drag & Drop. Dabei steht die schrittweise, ausführlich erläuterte programmiertechnische Umsetzung im Mittelpunkt.

    Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    eins, zwei, DREI.

    Tom Alby Web 2.0 – Konzepte, Anwendungen, Technologien 3., überarbeitete Auflage 280 Seiten. 75 Abb. ISBN 978-3-446-41449-5

    • Liefert einen Überblick über die Konzepte, die hinter dem Web 2.0 stecken • Stellt die wichtigsten Anwendungen des Web 2.0 dar • Analysiert Stärken und Schwächen der Ansätze und Geschäftsmodelle • Vermittelt das richtige Verständnis für die Bedeutung des Web 2.0 Das Buch von Tom Alby ist daher auch kein weiteres Buch zu Web 2.0Techniken. Vielmehr behandelt es die Konzepte, ohne die man die Vorteile des Web 2.0, die sich vor allem Unternehmen bieten, verschenkt. Tom Alby untersucht die unterschiedlichen Ansätze und Geschäftsmodelle von Web 2.0-Anwendungen – Wikis, Blogs und Podcasts, Social Software, Folksonomy und User Generated Software – und zeigt Gemeinsamkeiten wie auch Unterschiede auf.

    Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    Mehr Transparenz = mehr Erfolg

    Aden Google Analytics Implementieren. Interpretieren. Profitieren. 357 Seiten ISBN 978-3-446-41905-6

    Sicher haben Sie von Google Analytics schon gehört oder nutzen es bereits. Aber kennen Sie auch alle Feinheiten, Tipps und Tricks? Haben Sie das Tool wirklich perfekt implementiert und Ihren individuellen Bedürfnissen angepasst? Nutzen Sie es vollständig aus und leiten konkrete Aktionen aus den vorhandenen Zahlen ab? Timo Aden, ehemaliger Google-Mitarbeiter und Web-Analyse-Experte, stellt Ihnen in diesem Praxisbuch die vielfältigen Funktionen, die dieses Tool bietet, umfassend vor. Von nützlichen Hinweisen und technischen Kniffen bei der Implementierung und dem Tracking sämtlicher Online-Marketing-Aktivitäten, über die effektive Anwendung der Benutzeroberfläche und Berichte bis hin zur Ableitung von konkreten Aktionen - dieser Praxisleitfaden deckt sämtliche Bereiche von Google Analytics ab.

    Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    Der ultimative Leitfaden für die Praxis.

    Alby/Karzauninkat Suchmaschinenoptimierung 296 Seiten. 2. Auflage ISBN 978-3-446-41027-5

    • Webseiten für Google & Co. effizient optimieren • Die Autoren sind seit Jahren im internationalen Suchmaschinenbusiness tätig • Optimierung als Teil der Unternehmenskommunikation, nicht als isolierte technische Aufgabe • Solide, dauerhafte Optimierungskonzepte statt dubioser, kurzfristiger Tricks – Beispiele aus realen Projekten und Seminaren Dieses Buch liefert alle nötigen Informationen, damit die eigene Site in Suchmaschinen besser gefunden wird. Dazu gehört u.a. das Wissen, wie die Suchalgorithmen der Suchmaschinen bei der Entwicklung der Website optimal ausgenutzt werden, welche Form des Suchmaschinen-Marketings am besten geeignet ist, welche technischen Hürden es zu meistern gilt und mit welcher Webprogrammierung die besten Erfolge zu erzielen sind. Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    Wissen wie Hacker ticken!

    Zalewski Stille im Netz 320 Seiten. ISBN 978-3-446-40800-5

    Hacker gehen den Weg des geringsten Widerstandes und suchen dazu nach nicht alltägliche Angriffsszenarien. Diese finden sie genau dort, wo das Wissen um mögliche Gefahren fehlt. Deshalb liefert das Buch keine fertigen Lösungen für konkrete Gefahren und wiegt den Leser nicht in absoluter Sicherheit – denn die ist illusorisch. Es erklärt vielmehr, wie Hacker denken, wo und wie sie nach Lücken in Systemen suchen, welche Gefahrentypen es gibt und weshalb sie gefährlich werden können. Ziel ist es, den Leser dafür zu sensibilisieren, Bedrohungen zu erkennen und adäquat darauf zu reagieren. Denn nur wer sich in die Logik des Hackers hineinversetzt, so Zalewskis Überzeugung, kann Angriffe wirklich effektiv abwehren. Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    Für Alles-Wissen-Woller

    Breymann Der C++ -Programmierer

    C++ lernen – Professionell anwenden – Lösungen nutzen 963 Seiten. Mit DVD ISBN 978-3-446-41644-4

    Egal ob Sie C++ lernen wollen oder Ihre Kenntnisse in der Softwareentwicklung mit C++ vertiefen, in diesem Buch finden Sie, was Sie brauchen. C++-Neulinge erhalten eine motivierende Einführung in die Sprache C++. Die vielen Beispiele sind leicht nachzuvollziehen. Klassen und Objekte, Templates, STL und Exceptions sind bald keine Fremdwörter mehr für Sie. Als Profi finden Sie in diesem Buch fortgeschrittene Themen wie ThreadProgrammierung, Netzwerk-Programmierung mit Sockets und grafische Benutzungsoberflächen. Durch den Einsatz der Boost- und Qt-Libraries wird größtmögliche Portabilität erreicht. Weil Softwareentwicklung nicht nur Programmierung ist, finden Sie hier auch Themen für die professionelle Arbeit im Team.

    Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    Konzentrieren Sie sich auf das Wesentliche!

    Hunt/Thomas Der Pragmatische Programmierer 331 Seiten. ISBN 978-3-446-22309-7

    Der Pragmatische Programmierer veranschaulicht zahlreiche Best Practices der Softwareentwicklung mit Hilfe von Anekdoten, Beispielen und interessanten Analogien. Wer dieses Buch liest, lernt, · die Anwender zu begeistern, · die echten Anforderungen zu finden, · gegen Redundanz anzugehen, · dynamischen und anpassbaren Quelltext zu schreiben, · effektiv zu testen, · Teams von Pragmatischen Programmierern zu bilden und · durch Automatisierung sorgfältiger zu entwickeln. Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    Der ProjektmanagementKlassiker

    Tom DeMarco Der Termin 272 Seiten. ISBN 978-3-446-41439-6

    Mr. Tompkins, ein von einem Telekommunikationsriesen soeben entlassener Manager, hat die Aufgabe, sechs Softwareprodukte zu entwickeln. Dazu teilt Tompkins die ihm zur Verfügung stehende gigantische Entwicklungsmannschaft in achtzehn Teams auf - drei für jedes Produkt. Die Teams sind unterschiedlich groß und setzen verschiedene Methoden ein. Sie befinden sich im Wettlauf miteinander und haben einen gnadenlos engen Terminplan. Mit seinen Teams und der Hilfe zahlreicher Berater, die ihn unterstützen, stellt Mr. Tompkins die Managementmethoden auf den Prüfstand, die er im Laufe seines langen Managerlebens kennen gelernt hat. Jedes Kapitel endet mit einem Tagebucheintrag, der seine verblüffenden Erkenntnisse zusammenfasst. Mehr Informationen zu diesem Buch und zu unserem Programm unter www.hanser.de/computer

    TYPO3 GEKONNT ERWEITERN // ■ Behandelt alle wesentlichen Aspekte der Extensionentwicklung ■ Berücksichtigt topaktuell Extbase und Fluid ■ Nur soviel allgemeine TYPO3-Grundlagen wie nötig ■ Ideale Mischung aus Referenz und Lernbuch ■ Code und Lösungen unter http://downloads.hanser.de und www.typo3-backstage.de

    TYPO3-EXTENSIONS // TYPO3 spielt im Markt der Content Management Systeme eine zentrale Rolle. Mit ein Grund für diesen Erfolg sind die vielfältigen Möglichkeiten zur Erweiterung des Systems durch eigene Extensions.

    TIPP // Suchen Sie Lösungen für Probleme im Umgang mit TYPO3? Dann könnte Ihnen das „TYPO3-Kochbuch“ von Alexander Ebner und Patrick Lobacher gefallen, das ebenfalls bei Hanser erschienen ist. //

    alexander EBNER arbeitet als Webentwickler beim Münchner Reiseveranstalter FTI und setzt dort regelmäßig TYPO3 ein. patrick LOBACHER ist Geschäftsführer der Agentur typofaktum unternehmenskommunikation und berät seit 1996 Unternehmen beim Einsatz von Webtechnologien. Auch bernhard ULBRICH ist als Entwickler für FTI tätig, schwerpunktmäßig im Bereich TYPO3-Extensions. tobias HAUSER & christian WENZ sind Herausgeber der Content ManagementBibliothek bei Hanser und beschäftigen sich seit Mitte der Neunziger Jahre mit Webtechnologien. Dabei haben sie zahlreiche CMS- und Portal-Projekte realisiert. Ihre Erfahrungen geben sie als Trainer, Berater und Autoren an ihre Kunden und Leser weiter.

    www.hanser.de/computer ISBN 978-3-446-41557-7

    Web Developer, Webprogrammierer, Content Provider und Hoster, die TYPO3 einsetzen

    9

    783446 415577

    Systemvoraussetzungen für eBook-inside: Internet-Verbindung und eBookreader Adobe Digital Editions.

    Genau diesem Thema widmet sich dieses Buch. Es wendet sich an TYPO3-Anwender und -Entwickler, die mithilfe eigener Erweiterungen das TYPO3-Grundsystem leistungsfähiger machen möchten. Schritt für Schritt wird der Leser durch die Welt der Extensionprogrammierung für das TYPO3-Backend und -Frontend geleitet; dabei werden alle Aspekte sowohl der klassischen Extensionentwicklung behandelt als auch neue Ansätze wie Extbase und Fluid berücksichtigt. Unterstützt wird die Darstellung durch zahlreiche anschauliche Beispiele. Der Referenzteil nennt und erläutert sämtliche Referenzen, API‘s, Strukturen und Funktionen.

    Recommend Documents

    Breymann  Ulrich Breymann    9., neu bearbeitete Auflage Prof. D...

    2218.book Seite 1 Freitag, 14. Juli 2006 1:58 13 < Professionelle Websites > 2218.book Seite 2 Freitag, 14. Juli 2006...

    Robert Deg Basiswissen Public Relations Robert Deg Basiswissen Public Relations Professionelle Presseund Öffentlichk...

    Heide Otten Professionelle Beziehungen Theorie und Praxis der Balintgruppenarbeit Heide Otten Professionelle Beziehu...

    60030-9 U1+U4:Layout 1 31.03.2010 16:13 Uhr Seite 1 Know-how ist blau. Dr. Peter Kraft/Andreas Weyert Dr. Peter Kra...

    High-Linearity CMOS RF Front-End Circuits Yongwang Ding Ramesh Harjani iigh-Linearity CMOS t F Front-End Circuits - ...

    Michael Moesslang Professionelle Authentizität Michael Moesslang Professionelle Authentizität Warum ein Juwel glänzt...

    Klaus J. Aumayr Erfolgreiches Produktmanagement Klaus J. Aumayr Erfolgreiches Produktmanagement Tool-Box für das pro...

    Flash 5 ActionScript Jan Brücher Marc Hugo Flash 5 ActionScript Professionelle Praxislösungen für Programmierer und ...

    Dieter Kuck Aufsichtsräte und Beiräte in Deutschland Dieter Kuck Aufsichtsräte und Beiräte in Deutschland Rahmenbedi...