Diese Extension wird Dir viel, viel Zeit sparen. Zeit, die Du dann mit anderen Dinge verbringen musst. Mit Deinen Kindern, Deinem Ehepartner oder Deiner cholerischen Schwiegermutter. Anders gesagt: Dir könnten die Ausreden ausgehen.
Falls Du Zweifel hast, ob Du das durchhältst, sollten Du nnhelpers nicht installieren. Für Risiken und Nebenwirkungen, die durch gewonnene Zeit in Deinem Entwicklerleben auftreten könnten, übernehmen wir keine Haftung.
Was macht nnhelpers?
Diese Extension übernimmt den unschönen Teil Deines Entwickleralltags. Immer wenn Du denkst: "Verdammt, das muss doch irgendwie einfacher gehen" wirst Du Dich an nnhelpers erinnern. Die Extension sitzt in einem Backend-Modul, immer griffbereit – und (fast) immer mit einem einfachen Einzeiler, der Dein Problem löst.
Du willst Beispiele? Wunderbar.
Schon mal probiert, in Typo3 ein simples Upload-Formular zu bauen, bei dem Dein User im Frontend ein Bild hochladen kann? Klingt eigentlich nach einem Kinderspiel, oder?
Nun ja. Wäre es auch – gäbe es da nicht den "File Abstract Layer" (FAL).
Ein neues Domain-Model ist schnell gebaut und instanziiert. Aber wie bekommt man die Upload-Datei aus dem PHP tmp-Verzeichnis vernünftig in eine SysFileReference konvertiert, die sich an das Model hängen lässt?
Du stellt die Frage Google. Nach vielen Varianten Deiner Suchphrase landest Du auf der vielversprechenden Doku unter docs.typo3.org. Leider findest Du dort nur die ernüchternde Aussage: "Sorry, mein Lieber. Nicht unser Bier. Frag Helmut."
Du gibst also Helmut einen Besuch. Der macht einen prima Job. Viel Code. Super strukturiert und kommentiert. Eine knappe halbe Stunde versuchst Du, in seinem Repo genau das Stück destillierten Code zu identifiziern, das Du für Deinen Fall brauchst.
Für das Beispiel oben haben wir in der ersten Umsetzung knapp 4 Stunden gebraucht. Wir waren damals in der 6er Version von Typo3. Als dann Typo3 7 LTS veröffentlicht wurde, suchten wir erneut - im Core gab es neue, brilliante Ideen. Und in Version 8? Halleluja.
Immer wieder passten wir alle Extensions von uns an die neuen Konzepte der Typo3 Versionen an. Und jedes Mal schien uns das recht sinnfrei. Viel Zeit, die ich offen gesagt lieber mit anderen Dingen verbringe. Wenn es sein muss, auch mit meiner Schwiegermutter.
Wie lösen wir das heute?
Wir würde nnhelpers um Hilfe bitten.
Schauen wir mal ins Backend Module:
Das Backend Modul zeigt alle Methoden und ViewHelper übersichtlich gruppiert nach Themen
Wonach suchen wir?
Richtig. Es geht um FAL. Also: Runterscrollen wir zum Abschnitt FAL und schauen, was es im Angebot gibt|
Jede Methode hat eine ausführliche Erklärung und Beispiel.
setInModel() hört sich genau nach dem an, was wir suchen.
Meine Anwendung ist komplexer. Ich möchte, dass der User auch den Bildtitel und die Bildbeschreibung im Frontend eingeben kann. Oh – aber das scheint ja auch zu gehen:
Ihr habt Euch selbst ein Ei gelegt. Jetzt kann der User ja nicht mehrere Bilder mit jeweils einem Titel und einer Beschreibung hochladen. Ähm... Moment - was ist das?
Ja, aber mal ehrlich: Das ist häßlich. An der Uni habe ich gelernt, dass...
Richtig. Aber wenn es das Leben einfacher macht, ist uns das einfach egal.
Das hier ist eine Zeile Code, die Du Dir merken kannst. Die Benutzung ist intuitiv – sie folgt keinen Konventionen oder Paradigmen. Sie folgt Deiner Intuition.
Dir gefällt das alles nicht?
Fein. Dann kannst Du Dir den Quelltext klauen und daraus etwas eigenes bauen. Damit Du nicht in Dein Dateisystem abtauchen musst, haben wir auch hier mitgedacht: Ein Klick und Du blickst mitten ins Eingemachte.
Quelltext direkt im Backend anschauen.
Deine Entscheidung!
Stop thinking, start coding.
Installation
Die Installation läuft wie bei jeder Extension ab.
Du musst keine TypoScript-Templates hinzufügen. Da die Extension "nichts tut", außer einen Werkzeugkoffer an Methoden und Funktionen für Deine tägliche Arbeit bereitzustellen, wird es auch keine Konflikte mit anderen Extensions geben.
Du stehst auf den Extension Manager?
Drücke den "Erweiterung hinzufügen"-Button und suche Sie nach dem Extension Key nnhelpers.
Importiere die Extension aus dem Repository. Schau ins Backend-Modul.
Fang an zu coden. Hab Spaß.
Handarbeit ist Dein Ding?
Die aktuellste Version findest Du immer auf https://extensions.typo3.org/extension/nnhelpers/ zum direkten Download.
Lade die t3x- oder zip-Version herunter - und anschließend im Extension-Manager hoch.
Aktivieren, fertig.
composer ist Dein Freund?
Wenn Typo3 bei Dir im Composer-Modus läuft, findest Du die neueste Version auf packagist unter dem Key nng/nnhelpers.
Sorry, dass nng wie Angular klingt. Das ist ein ziemlich dämliches Akronym für Neunundneunziggrad (99°).
composer require nng/nnhelpers
Copied!
Nichts git übers GIT?
Klar, gi(b)t es auch dort. Um genau zu sein, bei Bitbucket.
Wenn es Dich glücklich macht, ziehe es Dir wie die "harten Jungs" über die Kommandozeile.
Wenn Du nnhelpers in Deiner eigenen Extension verwenden möchtest, denke daran, die Abhängigkeiten in der ext_emconf.php und composer.json zu definieren:
Das hier kommt in die ext_emconf.php Deiner Extension:
Diverse Methoden, um mit Arrays zu arbeiten wie mergen, bereinigen oder leere Werte zu entfernen.
Methoden, um ein Value eines assoziativen Arrays als Key zu verwenden.
Overview of Methods
\nn\t3::Arrays()->first();
Gibt das erste Element des Arrays zurück, ohne array_shift()
Methoden, um im Frontend zu prüfen, ob ein User im Typo3-Backend eingeloggt ist und z.B. Admin-Rechte besitzt.
Methoden, um einen Backend-User zu starten, falls er nicht existiert (z.B. während eines Scheduler-Jobs).
Overview of Methods
\nn\t3::BackendUser()->get();
Holt den aktuellen Backend-User.
Entspricht $GLOBALS['BE_USER'] in früheren Typo3-Versionen.
Prüft, ob ein BE-User eingeloggt ist.
Beispiel: Im Frontend bestimmte Inhalte nur zeigen, wenn der User im Backend eingeloggt ist.
Früher: $GLOBALS['TSFE']->beUserLogin
// Prüfen nach vollständiger Initialisierung des Front/Backends
\nn\t3::BackendUser()->isLoggedIn();
// Prüfen anhand des JWT, z.B. in einem eID-script vor Authentifizierung
\nn\t3::BackendUser()->isLoggedIn( $request );
Starte (faken) Backend-User.
Löst das Problem, das z.B. aus dem Scheduler bestimmte Funktionen
wie log() nicht möglich sind, wenn kein aktiver BE-User existiert.
Speichert userspezifische Einstellungen für den aktuell eingeloggten Backend-User.
Diese Einstellungen sind auch nach Logout/Login wieder für den User verfügbar.
Siehe \nn\t3::BackendUser()->getSettings('myext') zum Auslesen der Daten.
Prüft, ob ein BE-User eingeloggt ist.
Beispiel: Im Frontend bestimmte Inhalte nur zeigen, wenn der User im Backend eingeloggt ist.
Früher: $GLOBALS['TSFE']->beUserLogin
// Prüfen nach vollständiger Initialisierung des Front/Backends
\nn\t3::BackendUser()->isLoggedIn();
// Prüfen anhand des JWT, z.B. in einem eID-script vor Authentifizierung
\nn\t3::BackendUser()->isLoggedIn( $request );
Starte (faken) Backend-User.
Löst das Problem, das z.B. aus dem Scheduler bestimmte Funktionen
wie log() nicht möglich sind, wenn kein aktiver BE-User existiert.
Speichert userspezifische Einstellungen für den aktuell eingeloggten Backend-User.
Diese Einstellungen sind auch nach Logout/Login wieder für den User verfügbar.
Siehe \nn\t3::BackendUser()->getSettings('myext') zum Auslesen der Daten.
Methoden, zum Lesen und Schreiben in den Typo3 Cache.
Nutzt das Caching-Framework von Typo3, siehe EXT:nnhelpers/ext_localconf.php für Details
Overview of Methods
\nn\t3::Cache()->clear($identifier = NULL);
Löscht Caches.
Wird ein identifier angegeben, dann werden nur die Caches des spezifischen
identifiers gelöscht – sonst ALLE Caches aller Extensions und Seiten.
RAM-Caches
CachingFramework-Caches, die per \nn\t3::Cache()->set() gesetzt wurde
Datei-Caches, die per \nn\t3::Cache()->write() gesetzt wurde
// ALLE Caches löschen – auch die Caches anderer Extensions, der Seiten etc.
\nn\t3::Cache()->clear();
// Nur die Caches mit einem bestimmten Identifier löschen
\nn\t3::Cache()->clear('nnhelpers');
// RAM-Cache verwenden? TRUE als dritter Parameter setzen
\nn\t3::Cache()->set('myid', $dataToCache, true);
// Dauer des Cache auf 60 Minuten festlegen
\nn\t3::Cache()->set('myid', $dataToCache, 3600);
// Als key kann auch ein Array angegeben werden
\nn\t3::Cache()->set(['pid'=>1, 'uid'=>'7'], $html);
Copied!
@param mixed $indentifier String oder Array zum Identifizieren des Cache
@param mixed $data Daten, die in den Cache geschrieben werden sollen. (String oder Array)
@param mixed $useRamCachetrue: temporärer Cache in $GLOBALS statt Caching-Framework.
Schreibt eine PHP-Datei, die per $cache = require('...') geladen werden kann.
Angelehnt an viele Core-Funktionen und Extensions (z.B. mask), die statische PHP-Dateien
ins Filesystem legen, um performancelastige Prozesse wie Klassenpfade, Annotation-Parsing etc.
besser zu cachen. Nutzt bewußt nicht die Core-Funktionen, um jeglichen Overhead zu
vermeiden und größtmögliche Kompatibilität bei Core-Updates zu gewährleisten.
Löscht Caches.
Wird ein identifier angegeben, dann werden nur die Caches des spezifischen
identifiers gelöscht – sonst ALLE Caches aller Extensions und Seiten.
RAM-Caches
CachingFramework-Caches, die per \nn\t3::Cache()->set() gesetzt wurde
Datei-Caches, die per \nn\t3::Cache()->write() gesetzt wurde
// ALLE Caches löschen – auch die Caches anderer Extensions, der Seiten etc.
\nn\t3::Cache()->clear();
// Nur die Caches mit einem bestimmten Identifier löschen
\nn\t3::Cache()->clear('nnhelpers');
Copied!
@param string $identifier
@return void
Source Code
publicfunctionclear( $identifier = null ){
if (!$identifier) {
// ALLE TYPO3 Caches löschen, der über das CachingFramework registriert wurde$this->cacheManager->flushCaches();
} else {
// Spezifischen Cache löschenif ($cacheUtility = $this->cacheManager->getCache( $identifier )) {
$cacheUtility->flush();
}
}
if (!$identifier || $identifier == 'nnhelpers') {
// RAM Cache löschen
$GLOBALS['nnhelpers_cache'] = [];
// File-Cache löschen
$cacheDir = \nn\t3::Environment()->getVarPath() . "/cache/code/nnhelpers";
if (is_dir($cacheDir)) {
$iterator = new \DirectoryIterator($cacheDir);
foreach ($iterator as $file) {
if ($file->isFile() && $file->getExtension() === 'php') {
unlink($file->getPathname());
}
}
}
}
}
Copied!
Cache::clearPageCache()
\nn\t3::Cache()->clearPageCache($pid = NULL);
Löscht den Seiten-Cache. Alias zu \nn\t3::Page()->clearCache()
\nn\t3::Cache()->clearPageCache( 17 ); // Seiten-Cache für pid=17 löschen
\nn\t3::Cache()->clearPageCache(); // Cache ALLER Seiten löschen
Copied!
@param mixed $pid pid der Seite, deren Cache gelöscht werden soll oder leer lassen für alle Seite
// RAM-Cache verwenden? TRUE als dritter Parameter setzen
\nn\t3::Cache()->set('myid', $dataToCache, true);
// Dauer des Cache auf 60 Minuten festlegen
\nn\t3::Cache()->set('myid', $dataToCache, 3600);
// Als key kann auch ein Array angegeben werden
\nn\t3::Cache()->set(['pid'=>1, 'uid'=>'7'], $html);
Copied!
@param mixed $indentifier String oder Array zum Identifizieren des Cache
@param mixed $data Daten, die in den Cache geschrieben werden sollen. (String oder Array)
@param mixed $useRamCachetrue: temporärer Cache in $GLOBALS statt Caching-Framework.
Schreibt eine PHP-Datei, die per $cache = require('...') geladen werden kann.
Angelehnt an viele Core-Funktionen und Extensions (z.B. mask), die statische PHP-Dateien
ins Filesystem legen, um performancelastige Prozesse wie Klassenpfade, Annotation-Parsing etc.
besser zu cachen. Nutzt bewußt nicht die Core-Funktionen, um jeglichen Overhead zu
vermeiden und größtmögliche Kompatibilität bei Core-Updates zu gewährleisten.
Lädt den Content für eine bestimmte Spalte (colPos) und Seite.
Wird keine pageUid angegeben, verwendet er die aktuelle Seite.
Mit slide werden die Inhaltselement der übergeordnete Seite geholt, falls auf der angegeben Seiten kein Inhaltselement in der Spalte existiert.
Inhalt der colPos = 110 von der aktuellen Seite holen:
\nn\t3::Content()->column( 110 );
Copied!
Inhalt der colPos = 110 von der aktuellen Seite holen. Falls auf der aktuellen Seite kein Inhalt in der Spalte ist, den Inhalt aus der übergeordneten Seite verwenden:
\nn\t3::Content()->column( 110, true );
Copied!
Inhalt der colPos = 110 von der Seite mit id 99 holen:
\nn\t3::Content()->column( 110, 99 );
Copied!
Inhalt der colPos = 110 von der Seite mit der id 99 holen. Falls auf Seite 99 kein Inhalt in der Spalte ist, den Inhalt aus der übergeordneten Seite der Seite 99 verwenden:
Lädt die Daten eines tt_content-Element als einfaches Array:
\nn\t3::Content()->get( 1201 );
Copied!
Laden von Relationen (media, assets, ...)
\nn\t3::Content()->get( 1201, true );
Copied!
Übersetzungen / Localization:
Element NICHT automatisch übersetzen, falls eine andere Sprache eingestellt wurde
\nn\t3::Content()->get( 1201, false, false );
Copied!
Element in einer ANDEREN Sprache holen, als im Frontend eingestellt wurde.
Berücksichtigt die Fallback-Chain der Sprache, die in der Site-Config eingestellt wurde
\nn\t3::Content()->get( 1201, false, 2 );
Copied!
Element mit eigener Fallback-Chain holen. Ignoriert dabei vollständig die Chain,
die in der Site-Config definiert wurde.
Daten in einer ANDEREN Sprache holen, als im Frontend eingestellt wurde.
Berücksichtigt die Fallback-Chain der Sprache, die in der Site-Config eingestellt wurde
Lädt den Content für eine bestimmte Spalte (colPos) und Seite.
Wird keine pageUid angegeben, verwendet er die aktuelle Seite.
Mit slide werden die Inhaltselement der übergeordnete Seite geholt, falls auf der angegeben Seiten kein Inhaltselement in der Spalte existiert.
Inhalt der colPos = 110 von der aktuellen Seite holen:
\nn\t3::Content()->column( 110 );
Copied!
Inhalt der colPos = 110 von der aktuellen Seite holen. Falls auf der aktuellen Seite kein Inhalt in der Spalte ist, den Inhalt aus der übergeordneten Seite verwenden:
\nn\t3::Content()->column( 110, true );
Copied!
Inhalt der colPos = 110 von der Seite mit id 99 holen:
\nn\t3::Content()->column( 110, 99 );
Copied!
Inhalt der colPos = 110 von der Seite mit der id 99 holen. Falls auf Seite 99 kein Inhalt in der Spalte ist, den Inhalt aus der übergeordneten Seite der Seite 99 verwenden:
Lädt die Daten eines tt_content-Element als einfaches Array:
\nn\t3::Content()->get( 1201 );
Copied!
Laden von Relationen (media, assets, ...)
\nn\t3::Content()->get( 1201, true );
Copied!
Übersetzungen / Localization:
Element NICHT automatisch übersetzen, falls eine andere Sprache eingestellt wurde
\nn\t3::Content()->get( 1201, false, false );
Copied!
Element in einer ANDEREN Sprache holen, als im Frontend eingestellt wurde.
Berücksichtigt die Fallback-Chain der Sprache, die in der Site-Config eingestellt wurde
\nn\t3::Content()->get( 1201, false, 2 );
Copied!
Element mit eigener Fallback-Chain holen. Ignoriert dabei vollständig die Chain,
die in der Site-Config definiert wurde.
Daten in einer ANDEREN Sprache holen, als im Frontend eingestellt wurde.
Berücksichtigt die Fallback-Chain der Sprache, die in der Site-Config eingestellt wurde
Konvertieren von Arrays zu Models, Models zu JSONs, Arrays zu ObjectStorages,
Hex-Farben zu RGB und vieles mehr, was irgendwie mit Konvertieren von Dingen
zu tun hat.
Konvertiert eine für Menschen lesbare Angabe von Bytes/Megabytes in einen Byte-Integer.
Extrem tolerant, was Leerzeichen, Groß/Klein-Schreibung und Kommas statt Punkten angeht.
Kann auch automatisch FileReferences erzeugen.
In diesem Bespiel wird ein neues Model des Typs \Nng\Model\Name erzeugt und
danach in der Datenbank persistiert. Das Feld falMedia ist eine ObjectStorage
mit FileReferences. Die FileReferences werden automatisch erzeugt!
Hinweis
Um ein bereits existierendes Model mit Daten aus einem Array zu aktualisieren gibt
es die Methode $updatedModel = \nn\t3::Obj( $prevModel )->merge( $data );
Konvertiert eine für Menschen lesbare Angabe von Bytes/Megabytes in einen Byte-Integer.
Extrem tolerant, was Leerzeichen, Groß/Klein-Schreibung und Kommas statt Punkten angeht.
Kann auch automatisch FileReferences erzeugen.
In diesem Bespiel wird ein neues Model des Typs \Nng\Model\Name erzeugt und
danach in der Datenbank persistiert. Das Feld falMedia ist eine ObjectStorage
mit FileReferences. Die FileReferences werden automatisch erzeugt!
Hinweis
Um ein bereits existierendes Model mit Daten aus einem Array zu aktualisieren gibt
es die Methode $updatedModel = \nn\t3::Obj( $prevModel )->merge( $data );
| @return mixed
Source Code
publicfunctiontoModel( $className = null, $parentModel = null ){
$arr = $this->initialArgument;
$model = GeneralUtility::makeInstance( $className );
return \nn\t3::Obj($model)->merge( $arr );
# ToDo: Prüfen, warum der DataMapper hier nicht funktioniert. Model wird nicht persistiert!# $dataMapper = GeneralUtility::makeInstance(DataMapper::class);# return $dataMapper->map($model, [$arr]);
}
Seit TYPO3 12 können Cookies nicht einfach über $_COOKIE[] gesetzt werden.
Sie müssen stattdessen im Psr\Http\Message\ResponseInterface gesetzt werden.
Einen Cookie erzeugen - aber noch nicht an den Client senden.
Der Cookie wird erst in der Middleware gesetzt, siehe:
| \Nng\Nnhelpers\Middleware\ModifyResponse
Einen Cookie erzeugen - aber noch nicht an den Client senden.
Der Cookie wird erst in der Middleware gesetzt, siehe:
| \Nng\Nnhelpers\Middleware\ModifyResponse
Datenbank-Eintrag löschen. Klein und Fein.
Es kann entweder ein Tabellenname und die UID übergeben werden - oder ein Model.
Löschen eines Datensatzes per Tabellenname und uid oder einem beliebigen Constraint:
// Löschen anhand der uid
\nn\t3::Db()->delete('table', $uid);
// Löschen anhand eines eigenen Feldes
\nn\t3::Db()->delete('table', ['uid_local'=>$uid]);
// Eintrag komplett und unwiderruflich löschen (nicht nur per Flag deleted = 1 entfernen)
\nn\t3::Db()->delete('table', $uid, true);
Radikales entfernen aller Spuren eines Datensatzen inkl. der physischen SysFiles,
die mit dem Model verknüpft sind. Mit Vorsicht zu verwenden, da keine Relationen
auf das zu löschende Model geprüft werden.
Die Daten werden als Array zurückgegeben – das ist (leider) noch immer die absolute
performanteste Art, viele Datensätze aus einer Tabelle zu holen, da kein DataMapper
die einzelnen Zeilen parsen muss.
// Alle Datensätze holen. "hidden" wird berücksichtigt.
\nn\t3::Db()->findAll('fe_users');
// Auch Datensätze holen, die "hidden" sind
\nn\t3::Db()->findAll('fe_users', true);
Findet einen Eintrag anhand der UID.
Funktioniert auch, wenn Frontend noch nicht initialisiert wurden,
z.B. während AuthentificationService läuft oder im Scheduler.
Findet ALLE Einträge, die in der Spalte $column einen Wert aus dem Array $values enthält.
Funktioniert auch, wenn das Frontend noch nicht initialisiert wurden.
Alias zu \nn\t3::Db()->findByValues()
// SELECT FROM fe_users WHERE uid IN (1,2,3)
\nn\t3::Db()->findIn('fe_users', 'uid', [1,2,3]);
// SELECT FROM fe_users WHERE username IN ('david', 'martin')
\nn\t3::Db()->findIn('fe_users', 'username', ['david', 'martin']);
Findet ALLE Einträge, die in der Spalte $column NICHT einen Wert aus dem Array $values enthält.
Funktioniert auch, wenn das Frontend noch nicht initialisiert wurden.
// SELECT FROM fe_users WHERE uid NOT IN (1,2,3)
\nn\t3::Db()->findNotIn('fe_users', 'uid', [1,2,3]);
// SELECT FROM fe_users WHERE username NOT IN ('david', 'martin')
\nn\t3::Db()->findNotIn('fe_users', 'username', ['david', 'martin']);
"Repariert" die SysFileReferences für Modelle, die eine Property haben,
die statt einer ObjectStorage<FileReference> nur eine FileReference
referenzieren. Zum aktuellen Zeitpunkt ist es unklar, weshalb TYPO3 diese zwar
in der Tabelle sys_file_reference persistiert, aber das Feld tablenames
leert – bzw. uid_foreign nicht setzt. Bei einer ObjectStorage<FileReference>
tritt das Problem nicht auf.
// muss direkt nach dem persistieren des Models passieren
\nn\t3::Db()->fixFileReferencesForModel( $model );
Ein oder mehrere Domain-Model/Entity anhand einer uid holen.
Es kann eine einzelne $uid oder eine Liste von $uids übergeben werden.
Liefert das "echte" Model/Object inklusive aller Relationen,
analog zu einer Query über das Repository.
// Ein einzelnes Model anhand seiner uid holen
$model = \nn\t3::Db()->get( 1, \Nng\MyExt\Domain\Model\Name::class );
// Ein Array an Models anhand ihrer uids holen
$modelArray = \nn\t3::Db()->get( [1,2,3], \Nng\MyExt\Domain\Model\Name::class );
// Gibt auch hidden Models zurück
$modelArrayWithHidden = \nn\t3::Db()->get( [1,2,3], \Nng\MyExt\Domain\Model\Name::class, true );
Datenbank-Eintrag erstellen ODER einen vorhandenen Datensatz updaten.
Entscheidet selbstständig, ob der Eintrag per UPDATE oder INSERT in die Datenbank
eingefügt bzw. ein vorhandener Datensatz aktualisiert werden muss. Die Daten werden
direkt persistiert!
Beispiel für Übergabe eines Tabellennamens und eines Arrays:
// keine uid übergeben? Dann INSERT eines neuen Datensatzes
\nn\t3::Db()->save('table', ['bodytext'=>'...']);
// uid übergeben? Dann UPDATE vorhandener Daten
\nn\t3::Db()->save('table', ['uid'=>123, 'bodytext'=>'...']);
Copied!
Beispiel für Übergabe eines Domain-Models:
// neues Model? Wird per $repo->add() eingefügt
$model = new \My\Nice\Model();
$model->setBodytext('...');
$persistedModel = \nn\t3::Db()->save( $model );
// vorhandenes Model? Wird per $repo->update() aktualisiert
$model = $myRepo->findByUid(123);
$model->setBodytext('...');
$persistedModel = \nn\t3::Db()->save( $model );
Constraint für sys_file_reference zu einem QueryBuilder hinzufügen.
Beschränkt die Ergebnisse darauf, ob es eine FAL-Relation gibt.
$queryBuilder = \nn\t3::Db()->getQueryBuilder( $table );
// Nur Datensätze holen, die für falfield mindestes eine SysFileReference haben
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield' );
// ... die KEINE SysFileReference für falfield haben
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', false );
// ... die GENAU 2 SysFileReferences haben
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', 2 );
// ... die 2 oder weniger (less than or equal) SysFileReferences haben
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', 2, 'lte' );
Contraint auf Datensätze beschränken, die NICHT in eine der angegebenen Kategorien sind.
Gegenteil und Alias zu \nn\t3::Db()->setSysCategoryConstraint()
Constraint für sys_category / sys_category_record_mm zu einem QueryBuilder hinzufügen.
Beschränkt die Ergebnisse auf die angegebenen Sys-Categories-UIDs.
Sortiert Ergebnisse eines Queries nach einem Array und bestimmten Feld.
Löst das Problem, dass eine ->in()-Query die Ergebnisse nicht
in der Reihenfolge der übergebenen IDs liefert. Beispiel:
| $query->matching($query->in('uid', [3,1,2])); kommt nicht zwingend
in der Reihenfolge [3,1,2] zurück.
Eine "rohe" Query an die Datenbank absetzen.
Näher an der Datenbank geht nicht. Du bist für alles selbst verantwortlich.
Injections steht nur Deine (hoffentlich ausreichende :) Intelligenz entgegen.
Hilft z.B. bei Abfragen von Tabellen, die nicht Teil der Typo3 Installation sind und
daher über den normal QueryBuilder nicht erreicht werden könnten.
// Variablen IMMER über escapen!
$keyword = \nn\t3::Db()->quote('suchbegriff');
$rows = \nn\t3::Db()->statement( "SELECT FROM tt_news WHERE bodytext LIKE '%{$keyword}%'");
// oder besser gleich prepared statements verwenden:
$rows = \nn\t3::Db()->statement( 'SELECT FROM tt_news WHERE bodytext LIKE :str', ['str'=>"%{$keyword}%"] );
// Typen können übergeben werden (bei Array wird das automatisch ermittelt)
$rows = \nn\t3::Db()->statement( 'SELECT FROM tt_news WHERE uid IN (:uids)', ['uids'=>[1,2,3]], ['uids'=>Connection::PARAM_INT_ARRAY] );
Copied!
Bei einem SELECT Statement werden die Zeilen aus der Datenbank als Array zurückgegeben.
Bei allen anderen Statements (z.B. UPDATE oder DELETE) wird die Anzahl der betroffenen Zeilen zurückgegeben.
Datenbank-Eintrag aktualisieren. Schnell und einfach.
Das Update kann entweder per Tabellenname und Daten-Array passieren.
Oder man übergibt ein Model.
Beispiele:
// UPDATES table SET title='new' WHERE uid=1
\nn\t3::Db()->update('table', ['title'=>'new'], 1);
// UPDATES table SET title='new' WHERE uid IN (1,2,3)
\nn\t3::Db()->update('table', ['title'=>'new'], ['uid'=>[1,2,3]);
// UPDATE table SET title='new' WHERE email='david@99grad.de' AND pid=12
\nn\t3::Db()->update('table', ['title'=>'new'], ['email'=>'david@99grad.de', 'pid'=>12, ...]);
Copied!
Mit true statt einer $uid werden ALLE Datensätze der Tabelle geupdated.
// UPDATE table SET test='1' WHERE 1
\nn\t3::Db()->update('table', ['test'=>1], true);
Copied!
Statt einem Tabellenname kann auch ein einfach Model übergeben werden.
Das Repository wird automatisch ermittelt und das Model direkt persistiert.
Datenbank-Eintrag löschen. Klein und Fein.
Es kann entweder ein Tabellenname und die UID übergeben werden - oder ein Model.
Löschen eines Datensatzes per Tabellenname und uid oder einem beliebigen Constraint:
// Löschen anhand der uid
\nn\t3::Db()->delete('table', $uid);
// Löschen anhand eines eigenen Feldes
\nn\t3::Db()->delete('table', ['uid_local'=>$uid]);
// Eintrag komplett und unwiderruflich löschen (nicht nur per Flag deleted = 1 entfernen)
\nn\t3::Db()->delete('table', $uid, true);
Radikales entfernen aller Spuren eines Datensatzen inkl. der physischen SysFiles,
die mit dem Model verknüpft sind. Mit Vorsicht zu verwenden, da keine Relationen
auf das zu löschende Model geprüft werden.
Die Daten werden als Array zurückgegeben – das ist (leider) noch immer die absolute
performanteste Art, viele Datensätze aus einer Tabelle zu holen, da kein DataMapper
die einzelnen Zeilen parsen muss.
// Alle Datensätze holen. "hidden" wird berücksichtigt.
\nn\t3::Db()->findAll('fe_users');
// Auch Datensätze holen, die "hidden" sind
\nn\t3::Db()->findAll('fe_users', true);
Findet einen Eintrag anhand der UID.
Funktioniert auch, wenn Frontend noch nicht initialisiert wurden,
z.B. während AuthentificationService läuft oder im Scheduler.
Findet ALLE Einträge, die in der Spalte $column einen Wert aus dem Array $values enthält.
Funktioniert auch, wenn das Frontend noch nicht initialisiert wurden.
Alias zu \nn\t3::Db()->findByValues()
// SELECT FROM fe_users WHERE uid IN (1,2,3)
\nn\t3::Db()->findIn('fe_users', 'uid', [1,2,3]);
// SELECT FROM fe_users WHERE username IN ('david', 'martin')
\nn\t3::Db()->findIn('fe_users', 'username', ['david', 'martin']);
Findet ALLE Einträge, die in der Spalte $column NICHT einen Wert aus dem Array $values enthält.
Funktioniert auch, wenn das Frontend noch nicht initialisiert wurden.
// SELECT FROM fe_users WHERE uid NOT IN (1,2,3)
\nn\t3::Db()->findNotIn('fe_users', 'uid', [1,2,3]);
// SELECT FROM fe_users WHERE username NOT IN ('david', 'martin')
\nn\t3::Db()->findNotIn('fe_users', 'username', ['david', 'martin']);
"Repariert" die SysFileReferences für Modelle, die eine Property haben,
die statt einer ObjectStorage<FileReference> nur eine FileReference
referenzieren. Zum aktuellen Zeitpunkt ist es unklar, weshalb TYPO3 diese zwar
in der Tabelle sys_file_reference persistiert, aber das Feld tablenames
leert – bzw. uid_foreign nicht setzt. Bei einer ObjectStorage<FileReference>
tritt das Problem nicht auf.
// muss direkt nach dem persistieren des Models passieren
\nn\t3::Db()->fixFileReferencesForModel( $model );
Ein oder mehrere Domain-Model/Entity anhand einer uid holen.
Es kann eine einzelne $uid oder eine Liste von $uids übergeben werden.
Liefert das "echte" Model/Object inklusive aller Relationen,
analog zu einer Query über das Repository.
// Ein einzelnes Model anhand seiner uid holen
$model = \nn\t3::Db()->get( 1, \Nng\MyExt\Domain\Model\Name::class );
// Ein Array an Models anhand ihrer uids holen
$modelArray = \nn\t3::Db()->get( [1,2,3], \Nng\MyExt\Domain\Model\Name::class );
// Gibt auch hidden Models zurück
$modelArrayWithHidden = \nn\t3::Db()->get( [1,2,3], \Nng\MyExt\Domain\Model\Name::class, true );
Datenbank-Eintrag erstellen ODER einen vorhandenen Datensatz updaten.
Entscheidet selbstständig, ob der Eintrag per UPDATE oder INSERT in die Datenbank
eingefügt bzw. ein vorhandener Datensatz aktualisiert werden muss. Die Daten werden
direkt persistiert!
Beispiel für Übergabe eines Tabellennamens und eines Arrays:
// keine uid übergeben? Dann INSERT eines neuen Datensatzes
\nn\t3::Db()->save('table', ['bodytext'=>'...']);
// uid übergeben? Dann UPDATE vorhandener Daten
\nn\t3::Db()->save('table', ['uid'=>123, 'bodytext'=>'...']);
Copied!
Beispiel für Übergabe eines Domain-Models:
// neues Model? Wird per $repo->add() eingefügt
$model = new \My\Nice\Model();
$model->setBodytext('...');
$persistedModel = \nn\t3::Db()->save( $model );
// vorhandenes Model? Wird per $repo->update() aktualisiert
$model = $myRepo->findByUid(123);
$model->setBodytext('...');
$persistedModel = \nn\t3::Db()->save( $model );
Constraint für sys_file_reference zu einem QueryBuilder hinzufügen.
Beschränkt die Ergebnisse darauf, ob es eine FAL-Relation gibt.
$queryBuilder = \nn\t3::Db()->getQueryBuilder( $table );
// Nur Datensätze holen, die für falfield mindestes eine SysFileReference haben
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield' );
// ... die KEINE SysFileReference für falfield haben
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', false );
// ... die GENAU 2 SysFileReferences haben
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', 2 );
// ... die 2 oder weniger (less than or equal) SysFileReferences haben
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', 2, 'lte' );
Contraint auf Datensätze beschränken, die NICHT in eine der angegebenen Kategorien sind.
Gegenteil und Alias zu \nn\t3::Db()->setSysCategoryConstraint()
Constraint für sys_category / sys_category_record_mm zu einem QueryBuilder hinzufügen.
Beschränkt die Ergebnisse auf die angegebenen Sys-Categories-UIDs.
Sortiert Ergebnisse eines Queries nach einem Array und bestimmten Feld.
Löst das Problem, dass eine ->in()-Query die Ergebnisse nicht
in der Reihenfolge der übergebenen IDs liefert. Beispiel:
| $query->matching($query->in('uid', [3,1,2])); kommt nicht zwingend
in der Reihenfolge [3,1,2] zurück.
Eine "rohe" Query an die Datenbank absetzen.
Näher an der Datenbank geht nicht. Du bist für alles selbst verantwortlich.
Injections steht nur Deine (hoffentlich ausreichende :) Intelligenz entgegen.
Hilft z.B. bei Abfragen von Tabellen, die nicht Teil der Typo3 Installation sind und
daher über den normal QueryBuilder nicht erreicht werden könnten.
// Variablen IMMER über escapen!
$keyword = \nn\t3::Db()->quote('suchbegriff');
$rows = \nn\t3::Db()->statement( "SELECT FROM tt_news WHERE bodytext LIKE '%{$keyword}%'");
// oder besser gleich prepared statements verwenden:
$rows = \nn\t3::Db()->statement( 'SELECT FROM tt_news WHERE bodytext LIKE :str', ['str'=>"%{$keyword}%"] );
// Typen können übergeben werden (bei Array wird das automatisch ermittelt)
$rows = \nn\t3::Db()->statement( 'SELECT FROM tt_news WHERE uid IN (:uids)', ['uids'=>[1,2,3]], ['uids'=>Connection::PARAM_INT_ARRAY] );
Copied!
Bei einem SELECT Statement werden die Zeilen aus der Datenbank als Array zurückgegeben.
Bei allen anderen Statements (z.B. UPDATE oder DELETE) wird die Anzahl der betroffenen Zeilen zurückgegeben.
@param string $statement
@param array $params
@param array $types
@return mixed
Source Code
publicfunctionstatement( $statement = '', $params = [], $types = [] ){
$connection = $this->getConnection();
// exec / fetchAll --> @siehe https://bit.ly/3ltPF0S// set types automatically if params were usedforeach ($params as $key=>$val) {
// was type defined in arguments? then skipif (isset($types[$key])) {
continue;
}
// type not defined - and not array? then add typeif (!is_array($val)) {
if (is_numeric($val)) {
$types[$key] = Connection::PARAM_INT;
} else {
$types[$key] = Connection::PARAM_STR;
}
continue;
}
// type not defined and array?
$allNumeric = count(array_filter($val, 'is_numeric')) === count($val);
$types[$key] = $allNumeric ? Connection::PARAM_INT_ARRAY : Connection::PARAM_STR_ARRAY;
}
if (stripos($statement, 'select ') !== false) {
$result = $connection->fetchAllAssociative( $statement, $params, $types );
} else {
$result = $connection->executeStatement( $statement, $params, $types );
}
return $result;
}
Datenbank-Eintrag aktualisieren. Schnell und einfach.
Das Update kann entweder per Tabellenname und Daten-Array passieren.
Oder man übergibt ein Model.
Beispiele:
// UPDATES table SET title='new' WHERE uid=1
\nn\t3::Db()->update('table', ['title'=>'new'], 1);
// UPDATES table SET title='new' WHERE uid IN (1,2,3)
\nn\t3::Db()->update('table', ['title'=>'new'], ['uid'=>[1,2,3]);
// UPDATE table SET title='new' WHERE email='david@99grad.de' AND pid=12
\nn\t3::Db()->update('table', ['title'=>'new'], ['email'=>'david@99grad.de', 'pid'=>12, ...]);
Copied!
Mit true statt einer $uid werden ALLE Datensätze der Tabelle geupdated.
// UPDATE table SET test='1' WHERE 1
\nn\t3::Db()->update('table', ['test'=>1], true);
Copied!
Statt einem Tabellenname kann auch ein einfach Model übergeben werden.
Das Repository wird automatisch ermittelt und das Model direkt persistiert.
Prüft, ob Hash eines Passwortes und ein Passwort übereinstimmen.
Anwendung: Passwort-Hash eines fe_users in der Datenbank mit übergebenem Passwort
vergleichen.
Entschlüsselt einen String oder ein Array.
Zum Verschlüsseln der Daten kann \nn\t3::Encrypt()->encode() verwendet werden.
Siehe \nn\t3::Encrypt()->encode() für ein komplettes Beispiel.
Im Gegensatz zu \nn\t3::Encrypt()->hash() kann ein verschlüsselter Wert per \nn\t3::Encrypt()->decode()
wieder entschlüsselt werden. Diese Methods eignet sich daher nicht, um sensible Daten wie z.B. Passworte
in einer Datenbank zu speichern. Dennoch ist der Schutz relativ hoch, da selbst identische Daten, die mit
dem gleichen Salting-Key verschlüsselt wurden, unterschiedlich aussehen.
Für die Verschlüsselung wird ein Salting Key generiert und in dem Extension Manager von nnhelpers gespeichert.
Dieser Key ist für jede Installation einmalig. Wird er verändert, dann können bereits verschlüsselte Daten nicht
wieder entschlüsselt werden.
Gibt den Klassen-Names des aktuellen Hash-Algorithmus eines verschlüsselten Passwortes wieder,
z.B. um beim fe_user zu wissen, wie das Passwort in der DB verschlüsselt wurde.
Holt den Enryption / Salting Key aus der Extension Konfiguration für nnhelpers.
Falls im Extension Manager noch kein Key gesetzt wurde, wird er automatisch generiert
und in der LocalConfiguration.php gespeichert.
Prüft, ob Hash aktualisiert werden muss, weil er nicht dem aktuellen Verschlüsselungs-Algorithmus enspricht.
Beim Update von Typo3 in eine neue LTS wird gerne auch der Hashing-Algorithmus der Passwörter in der Datenbank
verbessert. Diese Methode prüft, ob der übergebene Hash noch aktuell ist oder aktualisert werden muss.
Gibt true zurück, falls ein Update erforderlich ist.
Session-Hash für fe_sessions.ses_id holen.
Enspricht dem Wert, der für den Cookie fe_typo_user in der Datenbank gespeichert wird.
In TYPO3 < v10 wird hier ein unveränderter Wert zurückgegeben. Ab TYPO3 v10 wird die Session-ID im
Cookie fe_typo_user nicht mehr direkt in der Datenbank gespeichert, sondern gehashed.
Siehe: TYPO3\CMS\Core\Session\Backend\DatabaseSessionBackend->hash().
Ein JWT (Json Web Token) erzeugen, signieren und base64-Encoded zurückgeben.
Nicht vergessen: Ein JWT ist zwar "fälschungssicher", weil der Signatur-Hash nur mit
dem korrekten Key/Salt erzeugt werden kann – aber alle Daten im JWT sind für jeden
durch base64_decode() einsehbar. Ein JWT eignet sich keinesfalls, um sensible Daten wie
Passwörter oder Logins zu speichern!
Ein JWT (Json Web Token) parsen und die Signatur überprüfen.
Falls die Signatur valide ist (und damit der Payload nicht manipuliert wurde), wird der
Payload zurückgegeben. Bei ungültiger Signatur wird FALSE zurückgegeben.
Prüft, ob Hash eines Passwortes und ein Passwort übereinstimmen.
Anwendung: Passwort-Hash eines fe_users in der Datenbank mit übergebenem Passwort
vergleichen.
Entschlüsselt einen String oder ein Array.
Zum Verschlüsseln der Daten kann \nn\t3::Encrypt()->encode() verwendet werden.
Siehe \nn\t3::Encrypt()->encode() für ein komplettes Beispiel.
Im Gegensatz zu \nn\t3::Encrypt()->hash() kann ein verschlüsselter Wert per \nn\t3::Encrypt()->decode()
wieder entschlüsselt werden. Diese Methods eignet sich daher nicht, um sensible Daten wie z.B. Passworte
in einer Datenbank zu speichern. Dennoch ist der Schutz relativ hoch, da selbst identische Daten, die mit
dem gleichen Salting-Key verschlüsselt wurden, unterschiedlich aussehen.
Für die Verschlüsselung wird ein Salting Key generiert und in dem Extension Manager von nnhelpers gespeichert.
Dieser Key ist für jede Installation einmalig. Wird er verändert, dann können bereits verschlüsselte Daten nicht
wieder entschlüsselt werden.
Gibt den Klassen-Names des aktuellen Hash-Algorithmus eines verschlüsselten Passwortes wieder,
z.B. um beim fe_user zu wissen, wie das Passwort in der DB verschlüsselt wurde.
Holt den Enryption / Salting Key aus der Extension Konfiguration für nnhelpers.
Falls im Extension Manager noch kein Key gesetzt wurde, wird er automatisch generiert
und in der LocalConfiguration.php gespeichert.
\nn\t3::Encrypt()->getSaltingKey();
Copied!
| @return string
Source Code
publicfunctiongetSaltingKey(){
if ($key = \nn\t3::Settings()->getExtConf('nnhelpers')['saltingKey'] ?? false) {
return $key;
}
$key = base64_encode(json_encode([
base64_encode(openssl_random_pseudo_bytes(32)),
base64_encode(openssl_random_pseudo_bytes(64))
]));
if (!\nn\t3::Settings()->setExtConf( 'nnhelpers', 'saltingKey', $key)) {
\nn\t3::Exception('Please first set the encryption key in the Extension-Manager for nnhelpers!');
}
return $key;
}
Copied!
Encrypt::hash()
\nn\t3::Encrypt()->hash($string = '');
Einfaches Hashing, z.B. beim Check einer uid gegen ein Hash.
Prüft, ob Hash aktualisiert werden muss, weil er nicht dem aktuellen Verschlüsselungs-Algorithmus enspricht.
Beim Update von Typo3 in eine neue LTS wird gerne auch der Hashing-Algorithmus der Passwörter in der Datenbank
verbessert. Diese Methode prüft, ob der übergebene Hash noch aktuell ist oder aktualisert werden muss.
Gibt true zurück, falls ein Update erforderlich ist.
Session-Hash für fe_sessions.ses_id holen.
Enspricht dem Wert, der für den Cookie fe_typo_user in der Datenbank gespeichert wird.
In TYPO3 < v10 wird hier ein unveränderter Wert zurückgegeben. Ab TYPO3 v10 wird die Session-ID im
Cookie fe_typo_user nicht mehr direkt in der Datenbank gespeichert, sondern gehashed.
Siehe: TYPO3\CMS\Core\Session\Backend\DatabaseSessionBackend->hash().
Ein JWT (Json Web Token) erzeugen, signieren und base64-Encoded zurückgeben.
Nicht vergessen: Ein JWT ist zwar "fälschungssicher", weil der Signatur-Hash nur mit
dem korrekten Key/Salt erzeugt werden kann – aber alle Daten im JWT sind für jeden
durch base64_decode() einsehbar. Ein JWT eignet sich keinesfalls, um sensible Daten wie
Passwörter oder Logins zu speichern!
Ein JWT (Json Web Token) parsen und die Signatur überprüfen.
Falls die Signatur valide ist (und damit der Payload nicht manipuliert wurde), wird der
Payload zurückgegeben. Bei ungültiger Signatur wird FALSE zurückgegeben.
Gibt die Standard-Sprache (Default Language) zurück. Bei TYPO3 ist das immer die Sprache mit der ID 0.
Die Sprachen müssen in der YAML site configuration festgelegt sein.
Gibt eine Liste der Sprachen zurück, die verwendet werden sollen, falls
z.B. eine Seite oder ein Element nicht in der gewünschten Sprache existiert.
Wichtig: Die Fallback-Chain enthält an erster Stelle die aktuelle bzw. in $langUid
übergebene Sprache.
// Einstellungen für aktuelle Sprache verwenden (s. Site-Config YAML)
\nn\t3::Environment()->getLanguageFallbackChain(); // --> z.B. [0] oder [1,0]// Einstellungen für eine bestimmte Sprache holen
\nn\t3::Environment()->getLanguageFallbackChain( 1 );
// --> [1,0] - falls Fallback in Site-Config definiert wurde und der fallbackMode auf "fallback" steht// --> [1] - falls es keinen Fallback gibt oder der fallbackMode auf "strict" steht
Maximale Upload-Größe für Dateien aus dem Frontend zurückgeben.
Diese Angabe ist der Wert, der in der php.ini festgelegt wurde und ggf.
über die .htaccess überschrieben wurde.
\nn\t3::Environment()->getPostMaxSize(); // z.B. '1048576' bei 1MB
Das ist ein Array mit allen Ordnern, die beim autoloading / Bootstrap von TYPO3 nach Klassen
geparsed werden müssen. In einer TYPO3 Extension ist das per default der Ordern Classes.
Die Liste wird von Composer/TYPO3 generiert.
Zurückgegeben wird ein array. Key ist Vendor\Namespace\, Wert ist ein Array mit Pfaden zu den Ordnern,
die rekursiv nach Klassen durchsucht werden. Es spielt dabei keine Rolle, ob TYPO3 im composer
mode läuft oder nicht.
Das aktuelle Site Object holen.
Über dieses Object kann z.B. ab TYPO3 9 auf die Konfiguration aus der site YAML-Datei zugegriffen werden.
Im Kontext einer MiddleWare ist evtl. die site noch nicht geparsed / geladen.
In diesem Fall kann der $request aus der MiddleWare übergeben werden, um die Site zu ermitteln.
Siehe auch \nn\t3::Settings()->getSiteConfig(), um die site-Konfiguration auszulesen.
Generiert einen virtuellen Frontend Request, der in jedem Context verwendet werden kann.
Initialisiert auch das Frontend TypoScript-Object und alle relevanten Objekte.
Absoluten Pfad zu dem /var-Verzeichnis von Typo3 holen.
Dieses Verzeichnis speichert temporäre Cache-Dateien.
Je nach Version von Typo3 und Installationstyp (Composer oder Non-Composer mode)
ist dieses Verzeichnis an unterschiedlichen Orten zu finden.
Gibt die Standard-Sprache (Default Language) zurück. Bei TYPO3 ist das immer die Sprache mit der ID 0.
Die Sprachen müssen in der YAML site configuration festgelegt sein.
Gibt eine Liste der Sprachen zurück, die verwendet werden sollen, falls
z.B. eine Seite oder ein Element nicht in der gewünschten Sprache existiert.
Wichtig: Die Fallback-Chain enthält an erster Stelle die aktuelle bzw. in $langUid
übergebene Sprache.
// Einstellungen für aktuelle Sprache verwenden (s. Site-Config YAML)
\nn\t3::Environment()->getLanguageFallbackChain(); // --> z.B. [0] oder [1,0]// Einstellungen für eine bestimmte Sprache holen
\nn\t3::Environment()->getLanguageFallbackChain( 1 );
// --> [1,0] - falls Fallback in Site-Config definiert wurde und der fallbackMode auf "fallback" steht// --> [1] - falls es keinen Fallback gibt oder der fallbackMode auf "strict" steht
Maximale Upload-Größe für Dateien aus dem Frontend zurückgeben.
Diese Angabe ist der Wert, der in der php.ini festgelegt wurde und ggf.
über die .htaccess überschrieben wurde.
\nn\t3::Environment()->getPostMaxSize(); // z.B. '1048576' bei 1MB
Das ist ein Array mit allen Ordnern, die beim autoloading / Bootstrap von TYPO3 nach Klassen
geparsed werden müssen. In einer TYPO3 Extension ist das per default der Ordern Classes.
Die Liste wird von Composer/TYPO3 generiert.
Zurückgegeben wird ein array. Key ist Vendor\Namespace\, Wert ist ein Array mit Pfaden zu den Ordnern,
die rekursiv nach Klassen durchsucht werden. Es spielt dabei keine Rolle, ob TYPO3 im composer
mode läuft oder nicht.
Das aktuelle Site Object holen.
Über dieses Object kann z.B. ab TYPO3 9 auf die Konfiguration aus der site YAML-Datei zugegriffen werden.
Im Kontext einer MiddleWare ist evtl. die site noch nicht geparsed / geladen.
In diesem Fall kann der $request aus der MiddleWare übergeben werden, um die Site zu ermitteln.
Siehe auch \nn\t3::Settings()->getSiteConfig(), um die site-Konfiguration auszulesen.
Generiert einen virtuellen Frontend Request, der in jedem Context verwendet werden kann.
Initialisiert auch das Frontend TypoScript-Object und alle relevanten Objekte.
publicfunctiongetSyntheticFrontendRequest( $pageUid = null ){
if (isset($this->SYNTHETIC_FE_REQUEST)) {
return$this->SYNTHETIC_FE_REQUEST;
}
// Resolve site + language + a page id (use site root as default)
$pageUid = $pageUid ?: (int) (\nn\t3::Page()->getSiteRoot()['uid'] ?? 0);
$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($pageUid);
$langId = $this->getLanguage();
$language = $site->getLanguageById($langId);
// Build base URI (fallback if site base is empty)
$base = (string)$site->getBase();
if ($base === '' || $base === '/') {
$base = $this->getBaseURL();
}
$uri = new Uri($base);
// Start request (URI first, then method in v13)
$queryParams = ['id' => $pageUid]; // make routing happy
$request = (new ServerRequest($uri, 'GET'))
->withAttribute('site', $site)
->withAttribute('language', $language)
->withQueryParams($queryParams);
// normalizedParams (what older code used to get from getIndpEnv)
$serverParams = [
'HTTP_HOST' => $uri->getHost() . ($uri->getPort() ? ':' . $uri->getPort() : ''),
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => $uri->getPath() === '' ? '/' : $uri->getPath(),
'QUERY_STRING' => http_build_query($queryParams),
'HTTPS' => $uri->getScheme() === 'https' ? 'on' : 'off',
'SERVER_PORT' => $uri->getPort() ?: ($uri->getScheme() === 'https' ? 443 : 80),
];
$normalized = NormalizedParams::createFromServerParams($serverParams);
$request = $request->withAttribute('normalizedParams', $normalized);
// routing (PageArguments) — minimal but sufficient
$pageType = 0;
$routing = new PageArguments($pageUid, $pageType, $queryParams);
$request = $request->withAttribute('routing', $routing);
$request = $request->withAttribute(
'applicationType',
SystemEnvironmentBuilder::REQUESTTYPE_FE
);
// ensure full TypoScript even in "cached" context
$request = \nn\t3::Tsfe()->softDisableCache($request);
// Tiny handler to capture the mutated request
$captured = null;
$handler = newclass($captured) implementsRequestHandlerInterface{
public ?\Psr\Http\Message\ServerRequestInterface $captured;
publicfunction__construct(&$_c){ $this->captured = &$_c; }
publicfunctionhandle(\Psr\Http\Message\ServerRequestInterface $request): \Psr\Http\Message\ResponseInterface{
$this->captured = $request;
returnnew HtmlResponse('');
}
};
// needed to prevent cross-contamination when in backend-context
$oldRequest = $GLOBALS['TYPO3_REQUEST'] ?? null;
// initialize FE controller (sets frontend.controller / $GLOBALS['TSFE'])
GeneralUtility::makeInstance(TypoScriptFrontendInitialization::class)->process($request, $handler);
$request = $handler->captured ?? $request;
// prepare TypoScript (attaches frontend.typoscript with FULL setup now)
GeneralUtility::makeInstance(PrepareTypoScriptFrontendRendering::class)->process($request, $handler);
$request = $handler->captured ?? $request;
$this->SYNTHETIC_FE_REQUEST = $request;
// reset to global request before capturing
$GLOBALS['TYPO3_REQUEST'] = $oldRequest;
return $request;
}
Copied!
Environment::getVarPath()
\nn\t3::Environment()->getVarPath();
Absoluten Pfad zu dem /var-Verzeichnis von Typo3 holen.
Dieses Verzeichnis speichert temporäre Cache-Dateien.
Je nach Version von Typo3 und Installationstyp (Composer oder Non-Composer mode)
ist dieses Verzeichnis an unterschiedlichen Orten zu finden.
Eine Datei zu einem FileReference-Object konvertieren und
an die Property oder ObjectStorage eines Models hängen.
Siehe auch: \nn\t3::Fal()->setInModel( $member, 'falslideshow', $imagesToSet ); mit dem
Array von mehreren Bildern an eine ObjectStorage gehängt werden können.
Löscht den Cache für die Bildgrößen eines FAL inkl. der umgerechneten Bilder
Wird z.B. der f:image-ViewHelper verwendet, werden alle berechneten Bildgrößen
in der Tabelle sys_file_processedfile gespeichert. Ändert sich das Originalbild,
wird evtl. noch auf ein Bild aus dem Cache zugegriffen.
@param string $storageConfig Pfad/Ordner, in die FAL-Datei gespeichert werden soll (z.B. 'fileadmin/projektdaten/')
@param string $srcFile Quelldatei, die in FAL umgewandelt werden soll (z.B. 'uploads/tx_nnfesubmit/beispiel.jpg')
Kann auch URL zu YouTube/Vimeo-Video sein (z.B. https://www.youtube.com/watch?v=7Bb5jXhwnRY)
@param boolean $keepSrcFile Quelldatei nur kopieren, nicht verschieben?
@param boolean $forceCreateNew Soll immer neue Datei erzeugt werden? Falls nicht, gibt er ggf. bereits existierendes File-Object zurück
Eine Datei zu einem FileReference-Object konvertieren und für attach() an ein vorhandenes
Model und Feld / Property vorbereiten. Die FileReference wird dabei nicht automatisch
an das Model gehängt. Um das FAL direkt in dem Model zu setzen, kann der Helper
| \nn\t3::Fal()->attach( $model, $field, $itemData ) verwendet werden.
Erstellt neuen Eintrag in sys_file
Sucht in allen sys_file_storage-Einträgen, ob der Pfad zum $file bereits als Storage existiert.
Falls nicht, wird ein neuer Storage angelegt.
Löscht die physischen Dateien für ein Model (oder ein einzelnes
Feld des Models) vom Server.
// ALLE Dateien des gesamten Models löschen
\nn\t3::Fal()->deleteForModel( $model );
// ALLE Dateien aus dem Feld "images" löschen
\nn\t3::Fal()->deleteForModel( $model, 'images' );
Löscht alle physischen Thumbnail-Dateien, die für ein Bild generiert wurden inkl.
der Datensätze in der Tabelle sys_file_processedfile.
Das Ursprungsbild, das als Argument $path übergeben wurde, wird dabei nicht gelöscht.
Das Ganze erzwingt das Neugenerieren der Thumbnails für ein Bild, falls sich z.B. das
Quellbild geändert hat aber der Dateiname gleich geblieben ist.
Weiterer Anwendungsfall: Dateien auf dem Server bereinigen, weil z.B. sensible, personenbezogene
Daten gelöscht werden sollen inkl. aller generierten Thumbnails.
Löscht ein SysFile (Datensatz aus Tabelle sys_file) und alle dazugehörigen SysFileReferences.
Eine radikale Art, um ein Bild komplett aus der Indizierung von Typo3 zu nehmen.
Die physische Datei wird nicht vom Server gelöscht!
Siehe \nn\t3::File()->unlink() zum Löschen der physischen Datei.
Siehe \nn\t3::Fal()->detach( $model, $field ); zum Löschen aus einem Model.
Leert eine ObjectStorage in einem Model oder entfernt ein
einzelnes Object vom Model oder einer ObjectStorage.
Im Beispiel kann image eine ObjectStorage oder eine einzelne FileReference sein:
Erzeugt ein FileRefence Objekt (Tabelle: sys_file_reference) und verknüpft es mit einem Datensatz.
Beispiel: Hochgeladenes JPG soll als FAL an tt_news-Datensatz angehängt werden
Parameter:
key
Beschreibung
| src
Pfad zur Quelldatei (kann auch http-Link zu YouTube-Video sein)
| dest
Pfad zum Zielordner (optional, falls Datei verschoben/kopiert werden soll)
| table
Ziel-Tabelle, dem die FileReference zugeordnet werden soll (z.B. tx_myext_domain_model_entry)
| title
Titel
| description
Beschreibung
| link
Link
| crop
Beschnitt
| table
Ziel-Tabelle, dem die FileReference zugeordnet werden soll (z.B. tx_myext_domain_model_entry)
| sorting
(int) Sortierung
| field
Column-Name der Ziel-Tabelle, dem die FileReference zugeordnet werden soll (z.B. image)
| uid
(int) uid des Datensatzes in der Zieltabelle (tx_myext_domain_model_entry.uid)
| pid
(int) pid des Datensatzes in der Zieltabelle
| cruser_id
cruser_id des Datensatzes in der Zieltabelle
| copy
src-Datei nicht verschieben sondern kopieren (default: true)
| forceNew
Im Zielordner neue Datei erzwingen (sonst wird geprüft, ob bereits Datei existiert) default: false
| single
Sicherstellen, dass gleiche FileReferenz nur 1x pro Datensatz verknüpft wird (default: true)
Holt / konvertiert in ein TYPO3CMSCoreResourceFileReference Object (sys_file_reference)
"Smarte" Variante zu \TYPO3\CMS\Extbase\Service\ImageService->getImage()
Berechnet ein Bild über maxWidth, maxHeight, cropVariant etc.
Gibt URI zum Bild als String zurück. Hilfreich bei der Berechnung von Thumbnails im Backend.
Alias zu \nn\t3::File()->process()
Ersetzt eine FileReference oder ObjectStorage in einem Model mit Bildern.
Typischer Anwendungsfall: Ein FAL-Bild soll über ein Upload-Formular im Frontend geändert
werden können.
Für jedes Bild wird geprüft, ob bereits eine FileReference im Model existiert.
Bestehende FileReferences werden nicht überschrieben, sonst würden evtl.
Bildunterschriften oder Cropping-Anweisungen verloren gehen!
Achtung! Das Model wird automatisch persistiert!
$newModel = new \My\Extension\Domain\Model\Example();
\nn\t3::Fal()->setInModel( $newModel, 'falslideshow', 'path/to/file.jpg' );
echo $newModel->getUid(); // Model wurde persistiert!
Copied!
Beispiel mit einer einfachen FileReference im Model:
Eine FileReference in ein Array konvertieren.
Enthält publicUrl, title, alternative, crop etc. der FileReference.
Alias zu \nn\t3::Obj()->toArray( $fileReference );
Eine Datei zu einem FileReference-Object konvertieren und
an die Property oder ObjectStorage eines Models hängen.
Siehe auch: \nn\t3::Fal()->setInModel( $member, 'falslideshow', $imagesToSet ); mit dem
Array von mehreren Bildern an eine ObjectStorage gehängt werden können.
Löscht den Cache für die Bildgrößen eines FAL inkl. der umgerechneten Bilder
Wird z.B. der f:image-ViewHelper verwendet, werden alle berechneten Bildgrößen
in der Tabelle sys_file_processedfile gespeichert. Ändert sich das Originalbild,
wird evtl. noch auf ein Bild aus dem Cache zugegriffen.
@param string $storageConfig Pfad/Ordner, in die FAL-Datei gespeichert werden soll (z.B. 'fileadmin/projektdaten/')
@param string $srcFile Quelldatei, die in FAL umgewandelt werden soll (z.B. 'uploads/tx_nnfesubmit/beispiel.jpg')
Kann auch URL zu YouTube/Vimeo-Video sein (z.B. https://www.youtube.com/watch?v=7Bb5jXhwnRY)
@param boolean $keepSrcFile Quelldatei nur kopieren, nicht verschieben?
@param boolean $forceCreateNew Soll immer neue Datei erzeugt werden? Falls nicht, gibt er ggf. bereits existierendes File-Object zurück
Eine Datei zu einem FileReference-Object konvertieren und für attach() an ein vorhandenes
Model und Feld / Property vorbereiten. Die FileReference wird dabei nicht automatisch
an das Model gehängt. Um das FAL direkt in dem Model zu setzen, kann der Helper
| \nn\t3::Fal()->attach( $model, $field, $itemData ) verwendet werden.
Erstellt neuen Eintrag in sys_file
Sucht in allen sys_file_storage-Einträgen, ob der Pfad zum $file bereits als Storage existiert.
Falls nicht, wird ein neuer Storage angelegt.
Löscht die physischen Dateien für ein Model (oder ein einzelnes
Feld des Models) vom Server.
// ALLE Dateien des gesamten Models löschen
\nn\t3::Fal()->deleteForModel( $model );
// ALLE Dateien aus dem Feld "images" löschen
\nn\t3::Fal()->deleteForModel( $model, 'images' );
Löscht alle physischen Thumbnail-Dateien, die für ein Bild generiert wurden inkl.
der Datensätze in der Tabelle sys_file_processedfile.
Das Ursprungsbild, das als Argument $path übergeben wurde, wird dabei nicht gelöscht.
Das Ganze erzwingt das Neugenerieren der Thumbnails für ein Bild, falls sich z.B. das
Quellbild geändert hat aber der Dateiname gleich geblieben ist.
Weiterer Anwendungsfall: Dateien auf dem Server bereinigen, weil z.B. sensible, personenbezogene
Daten gelöscht werden sollen inkl. aller generierten Thumbnails.
Löscht ein SysFile (Datensatz aus Tabelle sys_file) und alle dazugehörigen SysFileReferences.
Eine radikale Art, um ein Bild komplett aus der Indizierung von Typo3 zu nehmen.
Die physische Datei wird nicht vom Server gelöscht!
Siehe \nn\t3::File()->unlink() zum Löschen der physischen Datei.
Siehe \nn\t3::Fal()->detach( $model, $field ); zum Löschen aus einem Model.
Leert eine ObjectStorage in einem Model oder entfernt ein
einzelnes Object vom Model oder einer ObjectStorage.
Im Beispiel kann image eine ObjectStorage oder eine einzelne FileReference sein:
Erzeugt ein FileRefence Objekt (Tabelle: sys_file_reference) und verknüpft es mit einem Datensatz.
Beispiel: Hochgeladenes JPG soll als FAL an tt_news-Datensatz angehängt werden
Parameter:
key
Beschreibung
| src
Pfad zur Quelldatei (kann auch http-Link zu YouTube-Video sein)
| dest
Pfad zum Zielordner (optional, falls Datei verschoben/kopiert werden soll)
| table
Ziel-Tabelle, dem die FileReference zugeordnet werden soll (z.B. tx_myext_domain_model_entry)
| title
Titel
| description
Beschreibung
| link
Link
| crop
Beschnitt
| table
Ziel-Tabelle, dem die FileReference zugeordnet werden soll (z.B. tx_myext_domain_model_entry)
| sorting
(int) Sortierung
| field
Column-Name der Ziel-Tabelle, dem die FileReference zugeordnet werden soll (z.B. image)
| uid
(int) uid des Datensatzes in der Zieltabelle (tx_myext_domain_model_entry.uid)
| pid
(int) pid des Datensatzes in der Zieltabelle
| cruser_id
cruser_id des Datensatzes in der Zieltabelle
| copy
src-Datei nicht verschieben sondern kopieren (default: true)
| forceNew
Im Zielordner neue Datei erzwingen (sonst wird geprüft, ob bereits Datei existiert) default: false
| single
Sicherstellen, dass gleiche FileReferenz nur 1x pro Datensatz verknüpft wird (default: true)
Holt / konvertiert in ein TYPO3CMSCoreResourceFileReference Object (sys_file_reference)
"Smarte" Variante zu \TYPO3\CMS\Extbase\Service\ImageService->getImage()
Berechnet ein Bild über maxWidth, maxHeight, cropVariant etc.
Gibt URI zum Bild als String zurück. Hilfreich bei der Berechnung von Thumbnails im Backend.
Alias zu \nn\t3::File()->process()
Ersetzt eine FileReference oder ObjectStorage in einem Model mit Bildern.
Typischer Anwendungsfall: Ein FAL-Bild soll über ein Upload-Formular im Frontend geändert
werden können.
Für jedes Bild wird geprüft, ob bereits eine FileReference im Model existiert.
Bestehende FileReferences werden nicht überschrieben, sonst würden evtl.
Bildunterschriften oder Cropping-Anweisungen verloren gehen!
Achtung! Das Model wird automatisch persistiert!
$newModel = new \My\Extension\Domain\Model\Example();
\nn\t3::Fal()->setInModel( $newModel, 'falslideshow', 'path/to/file.jpg' );
echo $newModel->getUid(); // Model wurde persistiert!
Copied!
Beispiel mit einer einfachen FileReference im Model:
@param mixed $model Das Model, das geändert werden soll
@param string $fieldName Property (Feldname) der ObjectStorage oder FileReference
@param mixed $imagesToAdd String / Array mit Bildern
| @return mixed
Source Code
publicfunctionsetInModel( $model, $fieldName = '', $imagesToAdd = [] ){
if (!$model) \nn\t3::Exception( 'Parameter $model is not a Model' );
$objHelper = \nn\t3::Obj();
$repository = \nn\t3::Db()->getRepositoryForModel( $model );
// Sicher gehen, dass das Model bereits persistiert wurde – ohne uid keine FileReference!if (!$model->getUid()) {
if ($repository) {
$repository->add( $model );
}
\nn\t3::Db()->persistAll();
}
$modelUid = $model->getUid();
if (!$modelUid) returnfalse;
// Der passende Tabellen-Name in der DB zum Model
$modelTableName = $objHelper->getTableName( $model );
// Aktuellen Wert auslesen und ermitteln, ob das Feld eine FileReference oder ObjectStorage ist
$props = $objHelper->getProps( $model );
$fieldValue = $objHelper->prop( $model, $fieldName );
$isObjectStorage = is_a( $props[$fieldName], ObjectStorage::class, true );
$isSysFileReference = is_a($props[$fieldName], SysFileReference::class, true );
if ($isObjectStorage && !$fieldValue) {
$objHelper->set( $model, $fieldName, new ObjectStorage() );
}
// Array der bereits bestehenden FileReferences erzeugen mit Pfad zu Bildern als Key
$existingFileReferencesByPublicUrl = [];
if ($fieldValue) {
if (!$isObjectStorage) {
$fieldValue = [$fieldValue];
}
foreach ($fieldValue as $sysFileRef) {
$publicUrl = $sysFileRef->getOriginalResource()->getPublicUrl();
$existingFileReferencesByPublicUrl[$publicUrl] = $sysFileRef;
}
}
// Normalisieren der Pfadangaben zu den Bildern.// Aus 'pfad/zum/bild.jpg' wird ['publicUrl'=>'pfad/zum/bild.jpg']if (is_string($imagesToAdd)) {
$imagesToAdd = ['publicUrl'=>$imagesToAdd];
}
// Grundsätzlich mit Arrays arbeiten, vereinfacht die Logik unten.// Aus ['publicUrl'=>'pfad/zum/bild.jpg'] wird [['publicUrl'=>'pfad/zum/bild.jpg']]if (is_array($imagesToAdd) && isset($imagesToAdd['publicUrl'])) {
$imagesToAdd = [$imagesToAdd];
}
// sysFiles und sysFileReferences erlaubenif ($objHelper->isFalFile($imagesToAdd) || $objHelper->isFile($imagesToAdd)) {
$imagesToAdd = [$imagesToAdd];
}
// Aus ['01.jpg', '02.jpg', ...] wird [['publicUrl'=>'01.jpg'], ['publicUrl'=>'02.jpg'], ...]foreach ($imagesToAdd as $k=>$v) {
if (is_string($v)) {
$imagesToAdd[$k] = ['publicUrl'=>$v];
}
}
// Durch die Liste der neuen Bilder gehen ...foreach ($imagesToAdd as $n=>$imgObj) {
// Bereits ein falFile oder eine sysFileReferenceif ($objHelper->isFile($imgObj) || $objHelper->isFalFile($imgObj)) {
continue;
}
$imgToAdd = $imgObj['publicUrl'];
$publicUrl = \nn\t3::File()->stripPathSite( $imgToAdd );
// Falls bereits eine FileReference zu dem gleichen Bild existiert, diese verwenden
$value = $existingFileReferencesByPublicUrl[$publicUrl] ?? '' ?: $publicUrl;
// Falls das Bild noch nicht im Model existierte, eine neue FileReference erzeugenif (is_string($value)) {
$falParams = [
'src' => $value,
'title' => $imgObj['title'] ?? '',
'description' => $imgObj['description'] ?? '',
'link' => $imgObj['link'] ?? '',
'crop' => $imgObj['crop'] ?? '',
'pid' => $model->getPid(),
'uid' => $model->getUid(),
'table' => $modelTableName,
'field' => $fieldName,
];
$value = $this->fromFile( $falParams );
}
// Sollte etwas schief gegangen sein, ist $value == FALSEif ($value) {
$imagesToAdd[$n] = $value;
}
}
if ($isSysFileReference) {
// Feld ist eine SysFileReference (ohne ObjectStorage)
$objectToSet = $imagesToAdd[0] ?? false;
} elseif ($isObjectStorage) {
// Feld ist eine ObjectStorage: Neue ObjectStorage zum Ersetzen der bisherigen erzeugen
$objectToSet = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage;
foreach ($imagesToAdd as $n=>$imgToAdd) {
if ($objHelper->isFile( $imgToAdd )) {
$imgToAdd = \nn\t3::Fal()->fromFalFile( $imgToAdd );
}
$objectToSet->attach( $imgToAdd );
}
} else {
// Feld ist keine ObjectStorage: Also einfach die erste FileReference verwenden.
$objectToSet = array_shift($imagesToAdd);
if (!$objectToSet && $existingFileReferencesByPublicUrl) {
// FileReference soll entfernt werdenforeach ($existingFileReferencesByPublicUrl as $sysFileRef) {
$this->deleteSysFileReference( $sysFileRef );
}
}
}
// Property im Model aktualisierenif ($objectToSet) {
$objHelper->set( $model, $fieldName, $objectToSet );
}
// Model aktualisierenif ($repository) {
$repository->update( $model );
\nn\t3::Db()->update( $model );
}
return $model;
}
Copied!
Fal::toArray()
\nn\t3::Fal()->toArray($fileReference = NULL);
Eine FileReference in ein Array konvertieren.
Enthält publicUrl, title, alternative, crop etc. der FileReference.
Alias zu \nn\t3::Obj()->toArray( $fileReference );
Kopiert eine Datei.
Gibt false zurück, falls die Datei nicht kopiert werden konnte.
Gibt (neuen) Dateinamen zurück, falls das Kopieren erfolgreich war.
Download einer einzelnen Datei oder eines gezippten Archives.
Download als ZIP erfordert die PHP-Extension gmp. Falls Extension nicht vorhanden ist,
wird auf .tar-Variante ausgewichen. Bei Mac verwendet die Funktion aufgrund von
Sicherheitswarnungen des Finders grundsätzlich tar
Wird ein Array übergeben, wird ein tar/zip-Download gestartet.
Durch Übergabe eines assoziativen Arrays mit Dateiname als key und Pfad im Archiv als value
Kann die Datei- und Ordnerstruktur im zip-Archiv bestimmt werden.
Gibt den Pfad einer Datei anhand eines Dateinamens und der Storage wieder.
Beispiel:
\nn\t3::File()->getPath('media/bild.jpg', $storage);
// ==> gibt '/var/www/.../fileadmin/media/bild.jpg' zurück
\nn\t3::File()->getPath('fileadmin/media/bild.jpg');
// ==> gibt '/var/www/.../fileadmin/media/bild.jpg' zurück
Findet ein passendes sys_file_storage zu einem Datei- oder Ordnerpfad.
Durchsucht dazu alle sys_file_storage-Einträge und vergleicht,
ob der basePath des Storages zum Pfad der Datei passt.
\nn\t3::File()->getStorage('fileadmin/test/beispiel.txt');
\nn\t3::File()->getStorage( $falFile );
\nn\t3::File()->getStorage( $sysFileReference );
// gibt ResourceStorage mit basePath "fileadmin/" zurück
\nn\t3::File()->isForbidden('bild.jpg'); => gibt 'false' zurück
\nn\t3::File()->isForbidden('bild.xyz', ['xyz']); => gibt 'false' zurück
\nn\t3::File()->isForbidden('hack.php'); => gibt 'true' zurück
\nn\t3::File()->isForbidden('.htaccess'); => gibt 'true' zurück
Eine Upload-Datei ins Zielverzeichnis verschieben.
Kann absoluter Pfad zur tmp-Datei des Uploads sein – oder ein TYPO3\CMS\Core\Http\UploadedFile,
das sich im Controller über $this->request->getUploadedFiles() holen lässt.
Berechnet ein Bild über maxWidth, maxHeight etc.
Einfache Version von \nn\t3::File()->processImage()
Kann verwendet werden, wenn es nur um das Generieren von verkleinerten Bilder geht
ohne Berücksichtigung von Korrekturen der Kamera-Ausrichtung etc.
Da die Crop-Einstellungen in FileReference und nicht File gespeichert sind,
funktioniert cropVariant nur bei Übergabe einer FileReference.
Kann direkt nach dem upload_copy_move() aufgerufen werden.
Korrigiert die Ausrichtung des Bildes, die evtl. in EXIF-Daten gespeichert wurde.
Für einfach maxWidth-Anweis ungen die Methode \nn\t3::File()->process() verwenden.
Anweisungen für $processing:
| correctOrientation => Drehung korrigieren (z.B. weil Foto vom Smartphone hochgeladen wurde)
relativen Pfad (vom aktuellen Script aus) zum einer Datei / Verzeichnis zurück.
Wird kein Pfad angegeben, wird das Typo3-Root-Verzeichnis zurückgegeben.
Gibt den Suffix für einen bestimmten Mime-Type / Content-Type zurück.
Sehr reduzierte Variante – nur wenige Typen abgedeckt.
Umfangreiche Version: https://bit.ly/3B9KrNA
\nn\t3::File()->suffixForMimeType('image/jpeg'); => gibt 'jpg' zurück
Löscht eine Datei komplett vom Sever.
Löscht auch alle sys_file und sys_file_references, die auf die Datei verweisen.
Zur Sicherheit können keine PHP oder HTML Dateien gelöscht werden.
Kopiert eine Datei.
Gibt false zurück, falls die Datei nicht kopiert werden konnte.
Gibt (neuen) Dateinamen zurück, falls das Kopieren erfolgreich war.
Download einer einzelnen Datei oder eines gezippten Archives.
Download als ZIP erfordert die PHP-Extension gmp. Falls Extension nicht vorhanden ist,
wird auf .tar-Variante ausgewichen. Bei Mac verwendet die Funktion aufgrund von
Sicherheitswarnungen des Finders grundsätzlich tar
Wird ein Array übergeben, wird ein tar/zip-Download gestartet.
Durch Übergabe eines assoziativen Arrays mit Dateiname als key und Pfad im Archiv als value
Kann die Datei- und Ordnerstruktur im zip-Archiv bestimmt werden.
Gibt den Pfad einer Datei anhand eines Dateinamens und der Storage wieder.
Beispiel:
\nn\t3::File()->getPath('media/bild.jpg', $storage);
// ==> gibt '/var/www/.../fileadmin/media/bild.jpg' zurück
\nn\t3::File()->getPath('fileadmin/media/bild.jpg');
// ==> gibt '/var/www/.../fileadmin/media/bild.jpg' zurück
Findet ein passendes sys_file_storage zu einem Datei- oder Ordnerpfad.
Durchsucht dazu alle sys_file_storage-Einträge und vergleicht,
ob der basePath des Storages zum Pfad der Datei passt.
\nn\t3::File()->getStorage('fileadmin/test/beispiel.txt');
\nn\t3::File()->getStorage( $falFile );
\nn\t3::File()->getStorage( $sysFileReference );
// gibt ResourceStorage mit basePath "fileadmin/" zurück
Copied!
| @return ResourceStorage
Source Code
publicfunctiongetStorage($file, $createIfNotExists = false){
if (!is_string($file)) {
if (\nn\t3::Obj()->isFalFile($file) || \nn\t3::Obj()->isFile($file)) {
return $file->getStorage();
} elseif (\nn\t3::Obj()->isFileReference($file)) {
return $file->getOriginalResource()->getStorage();
}
returnfalse;
}
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
try {
$resource = $resourceFactory->retrieveFileOrFolderObject($file);
if ($resource && $resource->getStorage()?->getUid()) {
return $resource->getStorage();
}
} catch (\Exception $e) {
// File/folder not found in any storage
}
$allowCreateConf = \nn\t3::Settings()->getExtConf('nnhelpers')['autoCreateFilemounts'] ?? true;
if (!($allowCreateConf && $createIfNotExists)) {
\nn\t3::Exception("nnhelpers: Storage for file {$file} was not found and autocreate was disabled.");
}
$storageRepository = \nn\t3::Storage();
$file = ltrim($file, '/');
$dirname = $this->getFolder($file);
$uid = $storageRepository->createLocalStorage($dirname . ' (nnhelpers)', $dirname, 'relative');
$storageRepository->clearStorageRowCache();
$storage = $storageRepository->findByUid($uid);
if (!$storage) {
\nn\t3::Exception("nnhelpers: Error autocreating storage for file {$file}.");
}
return $storage;
}
Copied!
File::isAllowed()
\nn\t3::File()->isAllowed($filename = NULL);
Gibt an, ob der Dateityp erlaubt ist
\nn\t3::File()->isForbidden('bild.jpg'); => gibt 'true' zurück
\nn\t3::File()->isForbidden('hack.php'); => gibt 'false' zurück
\nn\t3::File()->isForbidden('bild.jpg'); => gibt 'false' zurück
\nn\t3::File()->isForbidden('bild.xyz', ['xyz']); => gibt 'false' zurück
\nn\t3::File()->isForbidden('hack.php'); => gibt 'true' zurück
\nn\t3::File()->isForbidden('.htaccess'); => gibt 'true' zurück
Eine Upload-Datei ins Zielverzeichnis verschieben.
Kann absoluter Pfad zur tmp-Datei des Uploads sein – oder ein TYPO3\CMS\Core\Http\UploadedFile,
das sich im Controller über $this->request->getUploadedFiles() holen lässt.
Berechnet ein Bild über maxWidth, maxHeight etc.
Einfache Version von \nn\t3::File()->processImage()
Kann verwendet werden, wenn es nur um das Generieren von verkleinerten Bilder geht
ohne Berücksichtigung von Korrekturen der Kamera-Ausrichtung etc.
Da die Crop-Einstellungen in FileReference und nicht File gespeichert sind,
funktioniert cropVariant nur bei Übergabe einer FileReference.
Kann direkt nach dem upload_copy_move() aufgerufen werden.
Korrigiert die Ausrichtung des Bildes, die evtl. in EXIF-Daten gespeichert wurde.
Für einfach maxWidth-Anweis ungen die Methode \nn\t3::File()->process() verwenden.
Anweisungen für $processing:
| correctOrientation => Drehung korrigieren (z.B. weil Foto vom Smartphone hochgeladen wurde)
| @return string
Source Code
publicfunctionprocessImage($filenameOrSysFile = '', $processing = []){
if (is_string($filenameOrSysFile)) {
if ($falFile = \nn\t3::Fal()->getFalFile($filenameOrSysFile)) {
$filenameOrSysFile = $falFile;
}
}
// Bereits berechnete Bildgrößen löschen
\nn\t3::Fal()->clearCache($filenameOrSysFile);
if (is_string($filenameOrSysFile)) {
$filename = $filenameOrSysFile;
} elseif (is_a($filenameOrSysFile, \TYPO3\CMS\Core\Resource\File::class)) {
$filename = $filenameOrSysFile->getPublicUrl();
}
if (!trim($filename)) return;
$pathSite = \nn\t3::Environment()->getPathSite();
$processing = \nn\t3::Arrays([
'correctOrientation' => true,
'maxWidth' => 6000,
'maxHeight' => 6000,
])->merge($processing);
$processingInstructions = [
'file' => $filename,
'file.' => [],
];
if ($maxWidth = $processing['maxWidth']) {
$processingInstructions['file.']['maxW'] = $maxWidth;
}
if ($maxHeight = $processing['maxHeight']) {
$processingInstructions['file.']['maxH'] = $maxHeight;
}
// EXIF-Daten vorhanden? Dann als JSON speichern, weil sie nach dem Processing verloren gehen würden.if (is_object($filenameOrSysFile)) {
$uid = $filenameOrSysFile->getUid();
$exif = \nn\t3::Db()->findByUid('sys_file', $uid)['exif'] ?? [];
} elseif ($exif = $this->getImageData($filename)) {
$exif = $this->extractExifData($filename);
}
// $exif['im'] enthält z.B. "-rotate 90" als ImageMagick Anweisungif ($exif['im'] && $processing['correctOrientation']) {
$processingInstructions['file.']['params'] = $exif['im'];
}
$processedImageFilename = \nn\t3::Tsfe()->cObjGetSingle('IMG_RESOURCE', $processingInstructions);
if ($processedImageFilename) {
\TYPO3\CMS\Core\Utility\GeneralUtility::upload_copy_move($pathSite . $processedImageFilename, $pathSite . $filename);
}
$exif = array_merge($this->getData($filename), ['file' => $filename]);
// Update der Meta-Daten für das Bildif (is_object($filenameOrSysFile)) {
\nn\t3::Fal()->updateMetaData($filenameOrSysFile);
}
return $exif;
}
Copied!
File::read()
\nn\t3::File()->read($src = NULL);
Holt den Inhalt einer Datei
\nn\t3::File()->read('fileadmin/text.txt');
Copied!
| @return string|boolean
Source Code
publicfunctionread($src = null){
if (!$src || !$this->exists($src)) return'';
$absPath = $this->absPath($src);
if (!$absPath) return'';
return file_get_contents($absPath);
}
Copied!
File::relPath()
\nn\t3::File()->relPath($path = '');
relativen Pfad (vom aktuellen Script aus) zum einer Datei / Verzeichnis zurück.
Wird kein Pfad angegeben, wird das Typo3-Root-Verzeichnis zurückgegeben.
Gibt den Suffix für einen bestimmten Mime-Type / Content-Type zurück.
Sehr reduzierte Variante – nur wenige Typen abgedeckt.
Umfangreiche Version: https://bit.ly/3B9KrNA
\nn\t3::File()->suffixForMimeType('image/jpeg'); => gibt 'jpg' zurück
Löscht eine Datei komplett vom Sever.
Löscht auch alle sys_file und sys_file_references, die auf die Datei verweisen.
Zur Sicherheit können keine PHP oder HTML Dateien gelöscht werden.
Fügt Optionen aus TypoScript zur Auswahl in ein FlexForm oder TCA ein.
<config>
<type>select</type>
<items type="array"></items>
<itemsProcFunc>nn\t3\Flexform->insertOptions</itemsProcFunc>
<typoscriptPath>plugin.tx_extname.settings.templates</typoscriptPath>
<!-- Alternativ: Settings aus PageTSConfig laden: -->
<pageconfigPath>tx_extname.colors</pageconfigPath>
<!-- Optional: Eigenen Key aus TypoScript verwenden -->
<customKey>value</customKey>
<insertEmpty>1</insertEmpty>
<insertEmptyLabel>Nichts</insertEmptyLabel>
<insertEmptyValue></insertEmptyValue>
<hideKey>1</hideKey>
</config>
Copied!
Beim Typoscript sind verschiedene Arten des Aufbaus erlaubt:
plugin.tx_extname.settings.templates {
# Direkte key => label Paare
small = Small Design
# ... oder: Label im Subarray gesetzt
mid {
label = Mid Design
}
# ... oder: Key im Subarray gesetzt, praktisch z.B. für CSS-Klassen10 {
label = Big Design
classes = big big-thing
}
# ... oder eine userFunc. Gibt eine der Varianten oben als Array zurück30 {
userFunc = nn\t3\Flexform->getOptions
}
}
Copied!
Die Auswahl kann im TypoScript auf bestimmte Controller-Actions beschränkt werden.
In diesem Beispiel wird die Option "Gelb" nur angezeigt, wenn in der switchableControllerAction
| Category->list gewählt wurde.
Fügt Optionen aus TypoScript zur Auswahl in ein FlexForm oder TCA ein.
<config>
<type>select</type>
<items type="array"></items>
<itemsProcFunc>nn\t3\Flexform->insertOptions</itemsProcFunc>
<typoscriptPath>plugin.tx_extname.settings.templates</typoscriptPath>
<!-- Alternativ: Settings aus PageTSConfig laden: -->
<pageconfigPath>tx_extname.colors</pageconfigPath>
<!-- Optional: Eigenen Key aus TypoScript verwenden -->
<customKey>value</customKey>
<insertEmpty>1</insertEmpty>
<insertEmptyLabel>Nichts</insertEmptyLabel>
<insertEmptyValue></insertEmptyValue>
<hideKey>1</hideKey>
</config>
Copied!
Beim Typoscript sind verschiedene Arten des Aufbaus erlaubt:
plugin.tx_extname.settings.templates {
# Direkte key => label Paare
small = Small Design
# ... oder: Label im Subarray gesetzt
mid {
label = Mid Design
}
# ... oder: Key im Subarray gesetzt, praktisch z.B. für CSS-Klassen10 {
label = Big Design
classes = big big-thing
}
# ... oder eine userFunc. Gibt eine der Varianten oben als Array zurück30 {
userFunc = nn\t3\Flexform->getOptions
}
}
Copied!
Die Auswahl kann im TypoScript auf bestimmte Controller-Actions beschränkt werden.
In diesem Beispiel wird die Option "Gelb" nur angezeigt, wenn in der switchableControllerAction
| Category->list gewählt wurde.
Benutzergruppen des aktuellen FE-Users als Array holen.
Die uids der Benutzergruppen werden im zurückgegebenen Array als Key verwendet.
// Minimalversion: Per default gibt Typo3 nur title, uid und pid zurück
\nn\t3::FrontendUser()->getCurrentUserGroups(); // [1 => ['title'=>'Gruppe A', 'uid' => 1, 'pid'=>5]]// Mit true kann der komplette Datensatz für die fe_user_group aus der DB gelesen werden
\nn\t3::FrontendUser()->getCurrentUserGroups( true ); // [1 => [... alle Felder der DB] ]
Benutzergruppen des aktuellen FE-User holen.
Alias zu \nn\t3::FrontendUser()->getCurrentUserGroups();
// nur title, uid und pid der Gruppen laden
\nn\t3::FrontendUser()->getGroups();
// kompletten Datensatz der Gruppen laden
\nn\t3::FrontendUser()->getGroups( true );
Prüft, ob der User aktuell als FE-User eingeloggt ist.
Früher: isset($GLOBALS['TSFE']) && $GLOBALS['TSFE']->loginUser
// Prüfen nach vollständiger Initialisierung des Front/Backends
\nn\t3::FrontendUser()->isLoggedIn();
// Prüfen anhand des JWT, z.B. in einem eID-script vor Authentifizierung
\nn\t3::FrontendUser()->isLoggedIn( $request );
Wandelt ein Array oder eine kommaseparierte Liste mit Benutzergrupen-UIDs in
| fe_user_groups-Daten aus der Datenbank auf. Prüft auf geerbte Untergruppe.
Wird keine sessionID übergeben, sucht Typo3 selbst nach der Session-ID des FE-Users.
Bei Aufruf dieser Methode aus einer MiddleWare sollte der Request mit übergeben werden.
Dadurch kann z.B. der globale $_COOKIE-Wert und der cookieParams.fe_typo_user im Request
vor Authentifizierung über typo3/cms-frontend/authentication in einer eigenen MiddleWare
gesetzt werden. Hilfreich, falls eine Crossdomain-Authentifizierung erforderlich ist (z.B.
per Json Web Token / JWT).
// Session-data für `shop` mit neuen Daten mergen (bereits existierende keys in `shop` werden nicht gelöscht)
\nn\t3::FrontendUser()->setSessionData('shop', ['a'=>1]);
// Session-data für `shop` überschreiben (`a` aus dem Beispiel oben wird gelöscht)
\nn\t3::FrontendUser()->setSessionData('shop', ['b'=>1], false);
Benutzergruppen des aktuellen FE-Users als Array holen.
Die uids der Benutzergruppen werden im zurückgegebenen Array als Key verwendet.
// Minimalversion: Per default gibt Typo3 nur title, uid und pid zurück
\nn\t3::FrontendUser()->getCurrentUserGroups(); // [1 => ['title'=>'Gruppe A', 'uid' => 1, 'pid'=>5]]// Mit true kann der komplette Datensatz für die fe_user_group aus der DB gelesen werden
\nn\t3::FrontendUser()->getCurrentUserGroups( true ); // [1 => [... alle Felder der DB] ]
Copied!
| @return array
Source Code
publicfunctiongetCurrentUserGroups( $returnRowData = false ){
if (!$this->isLoggedIn()) return [];
if (($user = $this->getFrontendUser() ?? null)) {
// Wenn wir ein Frontend haben...
$rawGroupData = $user->groupData ?? [];
$groupDataByUid = [];
foreach (($rawGroupData['uid'] ?? []) as $i=>$uid) {
$groupDataByUid[$uid] = [];
if ($returnRowData) {
$groupDataByUid[$uid] = \nn\t3::Db()->findByUid('fe_groups', $uid);
}
foreach ($rawGroupData as $field=>$arr) {
$groupDataByUid[$uid][$field] = $arr[$i];
}
}
return $groupDataByUid;
}
// ... oder in einem Kontext ohne Frontend sind (z.B. einer Middleware)
$context = GeneralUtility::makeInstance(Context::class);
$userAspect = $context->getAspect('frontend.user');
if (!$userAspect) return [];
$userGroups = $this->resolveUserGroups($userAspect->get('groupIds'));
if ($returnRowData) {
return \nn\t3::Arrays($userGroups)->key('uid')->toArray() ?: [];
} else {
return \nn\t3::Arrays($userGroups)->key('uid')->pluck(['uid', 'title', 'pid'])->toArray();
}
return [];
}
Benutzergruppen des aktuellen FE-User holen.
Alias zu \nn\t3::FrontendUser()->getCurrentUserGroups();
// nur title, uid und pid der Gruppen laden
\nn\t3::FrontendUser()->getGroups();
// kompletten Datensatz der Gruppen laden
\nn\t3::FrontendUser()->getGroups( true );
Prüft, ob der User aktuell als FE-User eingeloggt ist.
Früher: isset($GLOBALS['TSFE']) && $GLOBALS['TSFE']->loginUser
// Prüfen nach vollständiger Initialisierung des Front/Backends
\nn\t3::FrontendUser()->isLoggedIn();
// Prüfen anhand des JWT, z.B. in einem eID-script vor Authentifizierung
\nn\t3::FrontendUser()->isLoggedIn( $request );
Wandelt ein Array oder eine kommaseparierte Liste mit Benutzergrupen-UIDs in
| fe_user_groups-Daten aus der Datenbank auf. Prüft auf geerbte Untergruppe.
Wird keine sessionID übergeben, sucht Typo3 selbst nach der Session-ID des FE-Users.
Bei Aufruf dieser Methode aus einer MiddleWare sollte der Request mit übergeben werden.
Dadurch kann z.B. der globale $_COOKIE-Wert und der cookieParams.fe_typo_user im Request
vor Authentifizierung über typo3/cms-frontend/authentication in einer eigenen MiddleWare
gesetzt werden. Hilfreich, falls eine Crossdomain-Authentifizierung erforderlich ist (z.B.
per Json Web Token / JWT).
// Session-data für `shop` mit neuen Daten mergen (bereits existierende keys in `shop` werden nicht gelöscht)
\nn\t3::FrontendUser()->setSessionData('shop', ['a'=>1]);
// Session-data für `shop` überschreiben (`a` aus dem Beispiel oben wird gelöscht)
\nn\t3::FrontendUser()->setSessionData('shop', ['b'=>1], false);
Die Session-ID entspricht dem TYPO3 Cookie fe_typo_user. In der Regel gibt es für
jede Fe-User-Session einen Eintrag in der Tabelle fe_sessions. Bis zu Typo3 v10 entsprach
die Spalte ses_id exakt dem Cookie-Wert.
Ab Typo3 v10 wird der Wert zusätzlich gehashed.
Siehe auch \nn\t3::Encrypt()->hashSessionId( $sessionId );
Eine neue FrontenUser-Session in der Tabelle fe_sessions anlegen.
Es kann wahlweise die fe_users.uid oder der fe_users.username übergeben werden.
Der User wird dabei nicht automatisch eingeloggt. Stattdessen wird nur eine gültige Session
in der Datenbank angelegt und vorbereitet, die Typo3 später zur Authentifizierung verwenden kann.
Gibt die Session-ID zurück.
Die Session-ID entspricht hierbei exakt dem Wert im fe_typo_user-Cookie - aber nicht zwingend dem
Wert, der in fe_sessions.ses_id gespeichert wird. Der Wert in der Datenbank wird ab TYPO3 v11
gehashed.
Falls die Session mit einer existierenden SessionId erneut aufgebaut werden soll, kann als optionaler,
zweiter Parameter eine (nicht-gehashte) SessionId übergeben werden:
Die Session-ID entspricht dem TYPO3 Cookie fe_typo_user. In der Regel gibt es für
jede Fe-User-Session einen Eintrag in der Tabelle fe_sessions. Bis zu Typo3 v10 entsprach
die Spalte ses_id exakt dem Cookie-Wert.
Ab Typo3 v10 wird der Wert zusätzlich gehashed.
Siehe auch \nn\t3::Encrypt()->hashSessionId( $sessionId );
Eine neue FrontenUser-Session in der Tabelle fe_sessions anlegen.
Es kann wahlweise die fe_users.uid oder der fe_users.username übergeben werden.
Der User wird dabei nicht automatisch eingeloggt. Stattdessen wird nur eine gültige Session
in der Datenbank angelegt und vorbereitet, die Typo3 später zur Authentifizierung verwenden kann.
Gibt die Session-ID zurück.
Die Session-ID entspricht hierbei exakt dem Wert im fe_typo_user-Cookie - aber nicht zwingend dem
Wert, der in fe_sessions.ses_id gespeichert wird. Der Wert in der Datenbank wird ab TYPO3 v11
gehashed.
Falls die Session mit einer existierenden SessionId erneut aufgebaut werden soll, kann als optionaler,
zweiter Parameter eine (nicht-gehashte) SessionId übergeben werden:
Berechnungen und Konvertieren von Geopositionen und Daten.
Zum Umwandeln von Geo-Koordinaten in Adressdaten und umgekehrt, muss ein Google Maps ApiKey
erstellt werden und im Extension Manager für nnhelpers hinterlegt werden. Alternativ kann
beim Initialisieren ein eigener Api-Key angegeben werden:
nn\t3::Geo( $myApiKey )->getCoordinates('...');
Copied!
Overview of Methods
\nn\t3::Geo()->autoComplete($params = []);
Autocomplete Suche: Findet Adressen (Namen) anhand eines Suchwortes
Api-Key für Methoden in dieser Klasse holen.
Der Api-Key kann entweder beim Initialisieren von \nn\t3::Geo() angegeben werden
oder im Extension Manager für nnhelpers.
Api-Key für Methoden in dieser Klasse holen.
Der Api-Key kann entweder beim Initialisieren von \nn\t3::Geo() angegeben werden
oder im Extension Manager für nnhelpers.
Verwendet die Übersetzungen, die im xlf einer Extension angegeben sind.
Diese Dateien liegen standardmäßig unter EXT:extname/Resources/Private/Language/locallang.xlf
bzw. EXT:extname/Resources/Private/Language/de.locallang.xlf für die jeweilige Übersetzung.
// Einfaches Beispiel:
\nn\t3::LL()->get(''LLL:EXT:myext/Resources/Private/Language/locallang_db.xlf:my.identifier');
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress');
// Argumente im String ersetzen: 'Nach der %s kommt die %s' oder `Vor der %2$s kommt die %1$s'
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress', ['eins', 'zwei']);
// explode() des Ergebnisses an einem Trennzeichen
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress', null, ',');
// In andere Sprache als aktuelle Frontend-Sprache übersetzen
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress', null, null, 'en');
\nn\t3::LL()->get('LLL:EXT:myext/Resources/Private/Language/locallang_db.xlf:my.identifier', null, null, null, 'en');
Übersetzt einen Text per DeepL.
Ein API-Key muss im Extension Manager eingetragen werden.
DeepL erlaubt die Übersetzung von bis zu 500.000 Zeichen / Monat kostenfrei.
Verwendet die Übersetzungen, die im xlf einer Extension angegeben sind.
Diese Dateien liegen standardmäßig unter EXT:extname/Resources/Private/Language/locallang.xlf
bzw. EXT:extname/Resources/Private/Language/de.locallang.xlf für die jeweilige Übersetzung.
// Einfaches Beispiel:
\nn\t3::LL()->get(''LLL:EXT:myext/Resources/Private/Language/locallang_db.xlf:my.identifier');
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress');
// Argumente im String ersetzen: 'Nach der %s kommt die %s' oder `Vor der %2$s kommt die %1$s'
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress', ['eins', 'zwei']);
// explode() des Ergebnisses an einem Trennzeichen
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress', null, ',');
// In andere Sprache als aktuelle Frontend-Sprache übersetzen
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress', null, null, 'en');
\nn\t3::LL()->get('LLL:EXT:myext/Resources/Private/Language/locallang_db.xlf:my.identifier', null, null, null, 'en');
Übersetzt einen Text per DeepL.
Ein API-Key muss im Extension Manager eingetragen werden.
DeepL erlaubt die Übersetzung von bis zu 500.000 Zeichen / Monat kostenfrei.
Vergleicht zwei Objekte, gibt Array mit Unterschieden zurück.
Existiert eine Property von objA nicht in objB, wird diese ignoriert.
// gibt Array mit Unterschieden zurück
\nn\t3::Obj()->diff( $objA, $objB );
// ignoriert die Felder uid und title
\nn\t3::Obj()->diff( $objA, $objB, ['uid', 'title'] );
// Vergleicht NUR die Felder title und bodytext
\nn\t3::Obj()->diff( $objA, $objB, [], ['title', 'bodytext'] );
// Optionen
\nn\t3::Obj()->diff( $objA, $objB, [], [], ['ignoreWhitespaces'=>true, 'ignoreTags'=>true, 'ignoreEncoding'=>true] );
Copied!
@param mixed $objA Ein Object, Array oder Model
@param mixed $objB Das zu vergleichende Object oder Model
@param array $fieldsToIgnore Liste der Properties, die ignoriert werden können. Leer = keine
@param array $fieldsToCompare Liste der Properties, die verglichen werden sollen. Leer = alle
@param boolean $options Optionen / Toleranzen beim Vergleichen
includeMissing => auch fehlende Properties in $objB hinzufügen
Alle keys eines Objektes holen, die einen SETTER haben.
Im Gegensatz zu \nn\t3::Obj()->getKeys() werden nur die Property-Keys
zurückgegeben, die sich auch setzen lassen, z.B. über setNameDerProp()
Prüft, ob es sich bei einem Typ (string) um einen "einfachen" Typ handelt.
Einfache Typen sind alle Typen außer Models, Klassen etc. - also z.B. array, string, boolean etc.
Damit können sogar FileReferences geschrieben / überschrieben werden.
In diesem Beispiel wird $data mit einem existierende Model gemerged.
| falMedia ist im Beispiel eine ObjectStorage. Das erste Element in falMedia exisitert
bereits in der Datenbank (uid = 12). Hier wird nur der Titel aktualisiert.
Das zweite Element im Array (ohne uid) ist neu. Dafür wird automatisch eine neue
| sys_file_reference in der Datenbank erzeugt.
Hinweis
Um ein neues Model mit Daten aus einem Array zu erzeugen gibt
es die Methode $newModel = \nn\t3::Convert($data)->toModel( \My\Model\Name::class );
Git ein Array mit Infos zurück:
| type ist dabei nur gesetzt, falls es ein Array oder eine ObjectStorage ist.
| elementType ist immer der Typ des Models oder das TypeHinting der Variable
Vergleicht zwei Objekte, gibt Array mit Unterschieden zurück.
Existiert eine Property von objA nicht in objB, wird diese ignoriert.
// gibt Array mit Unterschieden zurück
\nn\t3::Obj()->diff( $objA, $objB );
// ignoriert die Felder uid und title
\nn\t3::Obj()->diff( $objA, $objB, ['uid', 'title'] );
// Vergleicht NUR die Felder title und bodytext
\nn\t3::Obj()->diff( $objA, $objB, [], ['title', 'bodytext'] );
// Optionen
\nn\t3::Obj()->diff( $objA, $objB, [], [], ['ignoreWhitespaces'=>true, 'ignoreTags'=>true, 'ignoreEncoding'=>true] );
Copied!
@param mixed $objA Ein Object, Array oder Model
@param mixed $objB Das zu vergleichende Object oder Model
@param array $fieldsToIgnore Liste der Properties, die ignoriert werden können. Leer = keine
@param array $fieldsToCompare Liste der Properties, die verglichen werden sollen. Leer = alle
@param boolean $options Optionen / Toleranzen beim Vergleichen
includeMissing => auch fehlende Properties in $objB hinzufügen
Alle keys eines Objektes holen, die einen SETTER haben.
Im Gegensatz zu \nn\t3::Obj()->getKeys() werden nur die Property-Keys
zurückgegeben, die sich auch setzen lassen, z.B. über setNameDerProp()
Prüft, ob es sich bei einem Typ (string) um einen "einfachen" Typ handelt.
Einfache Typen sind alle Typen außer Models, Klassen etc. - also z.B. array, string, boolean etc.
Damit können sogar FileReferences geschrieben / überschrieben werden.
In diesem Beispiel wird $data mit einem existierende Model gemerged.
| falMedia ist im Beispiel eine ObjectStorage. Das erste Element in falMedia exisitert
bereits in der Datenbank (uid = 12). Hier wird nur der Titel aktualisiert.
Das zweite Element im Array (ohne uid) ist neu. Dafür wird automatisch eine neue
| sys_file_reference in der Datenbank erzeugt.
Hinweis
Um ein neues Model mit Daten aus einem Array zu erzeugen gibt
es die Methode $newModel = \nn\t3::Convert($data)->toModel( \My\Model\Name::class );
| @return Object
Source Code
publicfunctionmerge( $model = null, $overlay = null ){
$overlay = $this->initialArgument !== null ? $model : $overlay;
$model = $this->initialArgument !== null ? $this->initialArgument : $model;
$schema = \nn\t3::Obj()->getClassSchema($model);
$modelProperties = $schema->getProperties();
if (!is_array($overlay)) return $model;
foreach ($overlay as $propName=>$value) {
if ($propInfo = $modelProperties[$propName] ?? false) {
// Typ für Property des Models, z.B. `string`
$propType = $this->get( $propInfo, 'type');
$isSysFile = is_a( $propType, \TYPO3\CMS\Core\Resource\File::class, true );
if ($this->isSimpleType($propType)) {
// -----// Es ist ein "einfacher Typ" (`string`, `int` etc.). Kann direkt gesetzt werden!$this->set( $model, $propName, $value );
continue;
}
if (!class_exists($propType)) {
\nn\t3::Exception( "Class of type `{$propType}` is not defined." );
}
$curPropValue = $this->get( $model, $propName );
if (!$isSysFile) {
// Es ist ein `Model`, `FileReference` etc.
$child = \nn\t3::newClass( $propType );
}
if ($isSysFile) {
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
$uid = false;
// '1'if (is_numeric($value)) {
$uid = $value;
}
// ['uid'=>1]if (!$uid && is_array($value)) {
$uid = $value['uid'] ?? false;
}
// 'index.php?eID=dumpFile&t=f&f=255&token=...' or 'https://www.website.com/fileadmin/file.txt'if (!$uid && is_string($value)) {
$queryParams = [];
$parsedUrl = parse_url($value);
parse_str($parsedUrl['query'], $queryParams);
if (($queryParams['eID'] ?? false) == 'dumpFile' && $uid = intval($queryParams['f'] ?? 0)) {
$value = $resourceFactory->getFileObject($uid);
} elseif ($parsedUrl['host'] ?? false) {
$value = ltrim($parsedUrl['path'], '/');
}
}
if ($uid) {
$value = $resourceFactory->getFileObject(intval($uid));
} else {
try {
// '/var/www/path/to/file.txt' or '1:/path/to/file.txt' or '/fileadmin/path/to/file.txt'
$value = $resourceFactory->getFileObjectFromCombinedIdentifier($value);
} catch( \Exception $e ) {
$value = null;
}
}
} elseif ($this->isFileReference($child)) {
// -----// Die Property ist eine einzelne `SysFileReference` – keine `ObjectStorage`
$curPublicUrl = \nn\t3::File()->getPublicUrl( $curPropValue );
$publicUrl = \nn\t3::File()->getPublicUrl( $value );
if ($curPublicUrl == $publicUrl) {
// An der URL hat sich nichts geändert. Bisherige `SysFileReference` weiter verwenden.
$value = $curPropValue;
} else {
// Neue URL. Falls bereits ein FAL am Model: Entfernenif ($this->isFileReference($curPropValue)) {
$persistenceManager = GeneralUtility::makeInstance( PersistenceManager::class );
$persistenceManager->remove( $curPropValue );
}
// ... und neues FAL erzeugenif ($value) {
\nn\t3::Fal()->attach( $model, $propName, $value );
continue;
} else {
$value = null;
}
}
} elseif ($this->isStorage($child)) {
// -----// Die Property ist eine `ObjectStorage`
$value = $this->forceArray( $value );
$childPropType = \nn\t3::Obj()->get($propInfo, 'elementType');
if (!class_exists($childPropType)) {
\nn\t3::Exception( "Class of type `{$childPropType}` is not defined." );
}
// sys_file stored in the ObjectStorage?
$isSysFile = is_a($childPropType, \TYPO3\CMS\Core\Resource\File::class, true);
$isFileReference = false;
if (!$isSysFile) {
$storageItemInstance = \nn\t3::newClass( $childPropType );
$isFileReference = $this->isFileReference( $storageItemInstance );
}
// Array der existierende Items in der `ObjectStorage` holen. Key ist `uid` oder `publicUrl`
$existingStorageItemsByUid = [];
if ($curPropValue) {
foreach ($curPropValue as $item) {
$uid = $isFileReference ? \nn\t3::File()->getPublicUrl( $item ) : $this->get( $item, 'uid' );
if (!isset($existingStorageItemsByUid)) {
$existingStorageItemsByUid[$uid] = [];
}
$existingStorageItemsByUid[$uid][] = $item;
}
}
$storageClassName = get_class($child);
$objectStorage = \nn\t3::newClass( $storageClassName );
// Jedes Item in die Storage einfügen. Dabei werden bereits vorhandene Items aus der alten Storage verwendet.foreach ($value as $itemData) {
$uid = false;
// `[1, ...]`if (is_numeric($itemData)) $uid = $itemData;
// `[['publicUrl'=>'bild.jpg'], ...]` oder `[['bild.jpg'], ...]`if (!$uid && $isFileReference) $uid = \nn\t3::File()->getPublicUrl( $itemData );
// `[['uid'=>'1'], ...]`if (!$uid) $uid = $this->get( $itemData, 'uid' );
// Gibt es das Item bereits? Dann vorhandenes Item verwenden, kein neues erzeugen!
$arrayReference = $existingStorageItemsByUid[$uid] ?? [];
$item = array_shift($arrayReference);
// Item bereits vorhanden?if ($item) {
// ... dann das bisherige Item verwenden.// $item = \nn\t3::Obj( $item )->merge( $itemData );
} elseif ($isSysFile) {
\nn\t3::Exception( "Converting to SysFile not supported yet." );
} elseif ($isFileReference) {
// sonst: Falls eine FileReference gewünscht ist, dann neu erzeugen!
$item = \nn\t3::Fal()->createForModel( $model, $propName, $itemData );
} elseif ($uid) {
// Alles AUSSER `FileReference` – und `uid` übergeben/bekannt? Dann das Model aus der Datenbank laden.
$item = \nn\t3::Db()->get( $uid, $childPropType );
// Model nicht in DB gefunden? Dann ignorieren.if (!$item) continue;
} else {
// Keine `FileReference` und KEINE `uid` übergeben? Dann neues Model erzeugen.
$item = \nn\t3::newClass( $childPropType );
}
// Model konnte nicht erzeugt / gefunden werden? Dann ignorieren!if (!$item) continue;
// Merge der neuen Overlay-Daten und ans Storage hängen
$item = \nn\t3::Obj( $item )->merge( $itemData );
$objectStorage->attach( $item );
}
$value = $objectStorage;
}
elseif ( is_a($child, \DateTime::class, true )) {
// -----// Die Property ist ein `DateTime`if ($value) {
$value = (new \DateTime())->setTimestamp( $value );
} else {
$value = null;
}
}
else {
// -----// Property enthält eine einzelne Relation, ist aber weder eine `FileReference` noch eine `ObjectStorage`if ($uid = is_numeric($value) ? $value : $this->get( $value, 'uid' )) {
$child = \nn\t3::Db()->get( $uid, get_class($child) );
if (!$child) $value = null;
}
if ($value) {
$value = \nn\t3::Obj( $child )->merge( $value );
}
}
$this->set( $model, $propName, $value );
}
}
return $model;
}
Copied!
Obj::parseType()
\nn\t3::Obj()->parseType($paramType = '');
Einen String mit Infos zu ObjectStorage<Model> parsen.
Git ein Array mit Infos zurück:
| type ist dabei nur gesetzt, falls es ein Array oder eine ObjectStorage ist.
| elementType ist immer der Typ des Models oder das TypeHinting der Variable
// data der aktuellen Seite
\nn\t3::Page()->getData();
// data der Seite mit pid = 123 holen
\nn\t3::Page()->getData( 123 );
// data der Seiten mit pids = 123 und 456 holen. Key des Arrays = pid
\nn\t3::Page()->getData( [123, 456] );
Einzelnes Feld aus page-Data holen.
Der Wert kann per slide = true von übergeordneten Seiten geerbt werden.
(!) Wichtig:
Eigene Felder müssen in der ext_localconf.php als rootLine definiert werden!
Siehe auch \nn\t3::Registry()->rootLineFields(['key', '...']);
Einen einfachen Link zu einer Seite im Frontend generieren.
Funktioniert in jedem Kontext - sowohl aus einem Backend-Modul oder Scheduler/CLI-Job heraus, als auch im Frontend-Kontext, z.B. im Controller oder einem ViewHelper.
Aus dem Backend-Kontext werden absolute URLs ins Frontend generiert. Die URLs werden als lesbare URLs kodiert - der Slug-Pfad bzw. RealURL werden berücksichtigt.
PID der aktuellen Seite holen.
Im Frontend: Die aktuelle TSFE->id
Im Backend: Die Seite, die im Seitenbaum ausgewählt wurde
Ohne Context: Die pid der site-Root
PID der Site-Root(s) holen.
Entspricht der Seite im Backend, die die "Weltkugel" als Symbol hat
(in den Seiteneigenschaften "als Anfang der Webseite nutzen")
// data der aktuellen Seite
\nn\t3::Page()->getData();
// data der Seite mit pid = 123 holen
\nn\t3::Page()->getData( 123 );
// data der Seiten mit pids = 123 und 456 holen. Key des Arrays = pid
\nn\t3::Page()->getData( [123, 456] );
Einzelnes Feld aus page-Data holen.
Der Wert kann per slide = true von übergeordneten Seiten geerbt werden.
(!) Wichtig:
Eigene Felder müssen in der ext_localconf.php als rootLine definiert werden!
Siehe auch \nn\t3::Registry()->rootLineFields(['key', '...']);
Einen einfachen Link zu einer Seite im Frontend generieren.
Funktioniert in jedem Kontext - sowohl aus einem Backend-Modul oder Scheduler/CLI-Job heraus, als auch im Frontend-Kontext, z.B. im Controller oder einem ViewHelper.
Aus dem Backend-Kontext werden absolute URLs ins Frontend generiert. Die URLs werden als lesbare URLs kodiert - der Slug-Pfad bzw. RealURL werden berücksichtigt.
PID der aktuellen Seite holen.
Im Frontend: Die aktuelle TSFE->id
Im Backend: Die Seite, die im Seitenbaum ausgewählt wurde
Ohne Context: Die pid der site-Root
PID der Site-Root(s) holen.
Entspricht der Seite im Backend, die die "Weltkugel" als Symbol hat
(in den Seiteneigenschaften "als Anfang der Webseite nutzen")
Liste mit 'ControllerName' => 'action,list,show' parsen.
Immer den vollen Klassen-Pfad in der ::class Schreibweise angeben.
Berücksichtigt, dass vor Typo3 10 nur der einfache Klassen-Name (z.B. Main)
als Key verwendet wird.
Ein Plugin registrieren zur Auswahl über das Dropdown CType im Backend.
In Configuration/TCA/Overrides/tt_content.php nutzen – oder ext_tables.php (veraltet).
Einen Wert in der Tabelle sys_registry speichern.
Daten in dieser Tabelle bleiben über die Session hinaus erhalten.
Ein Scheduler-Job kann z.B. speichern, wann er das letzte Mal
ausgeführt wurde.
Arrays werden per default rekursiv zusammengeführt / gemerged:
Liste mit 'ControllerName' => 'action,list,show' parsen.
Immer den vollen Klassen-Pfad in der ::class Schreibweise angeben.
Berücksichtigt, dass vor Typo3 10 nur der einfache Klassen-Name (z.B. Main)
als Key verwendet wird.
Ein Plugin registrieren zur Auswahl über das Dropdown CType im Backend.
In Configuration/TCA/Overrides/tt_content.php nutzen – oder ext_tables.php (veraltet).
Einen Wert in der Tabelle sys_registry speichern.
Daten in dieser Tabelle bleiben über die Session hinaus erhalten.
Ein Scheduler-Job kann z.B. speichern, wann er das letzte Mal
ausgeführt wurde.
Arrays werden per default rekursiv zusammengeführt / gemerged:
Holt das TypoScript-Setup und dort den Abschnitt "settings".
Werte aus dem FlexForm werden dabei nicht gemerged.
Alias zu \nn\t3::Settings()->getSettings().
Extension-Konfiguration holen.
Kommen aus der LocalConfiguration.php, werden über die Extension-Einstellungen
im Backend bzw. ext_conf_template.txt definiert
Das komplette TypoScript Setup holen, als einfaches Array - ohne "."-Syntax
Funktioniert sowohl im Frontend als auch Backend, mit und ohne übergebener pid
Merge aus TypoScript-Setup für ein Plugin und seinem Flexform holen.
Gibt das TypoScript-Array ab plugin.tx_extname.settings... zurück.
Wichtig: $extensionName nur angeben, wenn das Setup einer FREMDEN Extension
geholt werden soll oder es keinen Controller-Context gibt, weil der
Aufruf aus dem Backend gemacht wird... sonst werden die FlexForm-Werte nicht berücksichtigt!
Im FlexForm <settings.flexform.varName> verwenden!
| <settings.flexform.varName> überschreibt dann settings.varName im TypoScript-Setup
| $ttContentUidOrSetupArray kann uid eines tt_content-Inhaltselementes sein
oder ein einfaches Array zum Überschreiben der Werte aus dem TypoScript / FlexForm
\nn\t3::Settings()->getPlugin('extname') ergibt TypoScript ab plugin.tx_extname...
Copied!
Wichtig: $extensionName nur angeben, wenn das Setup einer FREMDEN Extension
geholt werden soll oder es keinen Controller-Context gibt, weil der Aufruf z.B.
aus dem Backend gemacht wird
Site-Konfiguration holen.
Das ist die Konfiguration, die ab TYPO3 9 in den YAML-Dateien im Ordner /sites definiert wurden.
Einige der Einstellungen sind auch über das Seitenmodul "Sites" einstellbar.
Im Kontext einer MiddleWare ist evtl. die site noch nicht geparsed / geladen.
In diesem Fall kann der $request aus der MiddleWare übergeben werden, um die Site zu ermitteln.
Aktuelle (ERSTE) StoragePid für das aktuelle Plugin holen.
Gespeichert im TypoScript-Setup der Extension unter
| plugin.tx_extname.persistence.storagePid bzw. im
FlexForm des Plugins auf der jeweiligen Seite.
WICHTIG: Merge mit gewählter StoragePID aus dem FlexForm
passiert nur, wenn $extNameleer gelassen wird.
ALLE storagePids für das aktuelle Plugin holen.
Gespeichert als komma-separierte Liste im TypoScript-Setup der Extension unter
| plugin.tx_extname.persistence.storagePid bzw. im
FlexForm des Plugins auf der jeweiligen Seite.
WICHTIG: Merge mit gewählter StoragePID aus dem FlexForm
passiert nur, wenn $extNameleer gelassen wird.
Auch die child-PageUids holen?
| true nimmt den Wert für "Rekursiv" aus dem FlexForm bzw. aus dem
TypoScript der Extension von plugin.tx_extname.persistence.recursive
Extension-Konfiguration schreiben.
Schreibt eine Extension-Konfiguration in die LocalConfiguration.php. Die Werte können bei
entsprechender Konfiguration in der ext_conf_template.txt auch über den Extension-Manager / die
Extension Konfiguration im Backend bearbeitet werden.
Holt das TypoScript-Setup und dort den Abschnitt "settings".
Werte aus dem FlexForm werden dabei nicht gemerged.
Alias zu \nn\t3::Settings()->getSettings().
publicfunctiongetConstants( $tsPath = '' ){
$constants = [];
if ($request = $GLOBALS['TYPO3_REQUEST'] ?? false) {
if ($ts = $request->getAttribute('frontend.typoscript')) {
try {
$constants = $ts->getSettingsTree()->toArray();
} catch ( \Exception $e ) {
// this might be related to https://forge.typo3.org/projects/typo3cms-core/issues
\nn\t3::Exception('TypoScript-Setup could not be loaded. If you are trying to access it from a Middleware or the CLI, try using $request->getAttribute(\'frontend.controller\')->config[\'INTincScript\'][] = []; in your Middleware to disable caching.');
}
}
}
$config = \nn\t3::TypoScript()->convertToPlainArray( $constants );
return $tsPath ? $this->getFromPath( $tsPath, $config ) : $config;
}
Copied!
Settings::getExtConf()
\nn\t3::Settings()->getExtConf($extName = '');
Extension-Konfiguration holen.
Kommen aus der LocalConfiguration.php, werden über die Extension-Einstellungen
im Backend bzw. ext_conf_template.txt definiert
Das komplette TypoScript Setup holen, als einfaches Array - ohne "."-Syntax
Funktioniert sowohl im Frontend als auch Backend, mit und ohne übergebener pid
publicfunctiongetFullTyposcript( $pid = null ){
if ($this->typoscriptSetupCache) return$this->typoscriptSetupCache;
$setup = false;
try {
$setup = $this->parseTypoScriptForPage($pid);
if (!$setup) {
$setup = $this->getFullTypoScriptFromConfigurationManager();
}
} catch ( \Exception $e ) {
// this might be related to https://forge.typo3.org/projects/typo3cms-core/issues
$setup = false;
}
if (!$setup) {
\nn\t3::Exception('TypoScript-Setup could not be loaded. If you are trying to access it from a Middleware or the CLI, try using $request->getAttribute(\'frontend.controller\')->config[\'INTincScript\'][] = []; in your Middleware to disable caching.');
}
$this->typoscriptSetupCache = \nn\t3::TypoScript()->convertToPlainArray($setup);
return$this->typoscriptSetupCache;
}
Merge aus TypoScript-Setup für ein Plugin und seinem Flexform holen.
Gibt das TypoScript-Array ab plugin.tx_extname.settings... zurück.
Wichtig: $extensionName nur angeben, wenn das Setup einer FREMDEN Extension
geholt werden soll oder es keinen Controller-Context gibt, weil der
Aufruf aus dem Backend gemacht wird... sonst werden die FlexForm-Werte nicht berücksichtigt!
Im FlexForm <settings.flexform.varName> verwenden!
| <settings.flexform.varName> überschreibt dann settings.varName im TypoScript-Setup
| $ttContentUidOrSetupArray kann uid eines tt_content-Inhaltselementes sein
oder ein einfaches Array zum Überschreiben der Werte aus dem TypoScript / FlexForm
\nn\t3::Settings()->getPlugin('extname') ergibt TypoScript ab plugin.tx_extname...
Copied!
Wichtig: $extensionName nur angeben, wenn das Setup einer FREMDEN Extension
geholt werden soll oder es keinen Controller-Context gibt, weil der Aufruf z.B.
aus dem Backend gemacht wird
| @return array
Source Code
publicfunctiongetPlugin($extName = null){
if (!$extName) {
try {
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
$setup = $configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK) ?: [];
return $setup;
} catch ( \Exception $e ) {
// silence is golden
}
}
// Fallback: Setup für das Plugin aus globaler TS-Konfiguration holen
$setup = $this->getFullTyposcript();
if (!$setup || !($setup['plugin'] ?? false)) return [];
if (isset($setup['plugin'][$extName])) {
return $setup['plugin'][$extName];
}
if (isset($setup['plugin']["tx_{$extName}"])) {
return $setup['plugin']["tx_{$extName}"];
}
return $setup['plugin']["tx_{$extName}_{$extName}"] ?? [];
}
Site-Konfiguration holen.
Das ist die Konfiguration, die ab TYPO3 9 in den YAML-Dateien im Ordner /sites definiert wurden.
Einige der Einstellungen sind auch über das Seitenmodul "Sites" einstellbar.
Im Kontext einer MiddleWare ist evtl. die site noch nicht geparsed / geladen.
In diesem Fall kann der $request aus der MiddleWare übergeben werden, um die Site zu ermitteln.
Aktuelle (ERSTE) StoragePid für das aktuelle Plugin holen.
Gespeichert im TypoScript-Setup der Extension unter
| plugin.tx_extname.persistence.storagePid bzw. im
FlexForm des Plugins auf der jeweiligen Seite.
WICHTIG: Merge mit gewählter StoragePID aus dem FlexForm
passiert nur, wenn $extNameleer gelassen wird.
ALLE storagePids für das aktuelle Plugin holen.
Gespeichert als komma-separierte Liste im TypoScript-Setup der Extension unter
| plugin.tx_extname.persistence.storagePid bzw. im
FlexForm des Plugins auf der jeweiligen Seite.
WICHTIG: Merge mit gewählter StoragePID aus dem FlexForm
passiert nur, wenn $extNameleer gelassen wird.
Auch die child-PageUids holen?
| true nimmt den Wert für "Rekursiv" aus dem FlexForm bzw. aus dem
TypoScript der Extension von plugin.tx_extname.persistence.recursive
Extension-Konfiguration schreiben.
Schreibt eine Extension-Konfiguration in die LocalConfiguration.php. Die Werte können bei
entsprechender Konfiguration in der ext_conf_template.txt auch über den Extension-Manager / die
Extension Konfiguration im Backend bearbeitet werden.
Gibt den Folder-Object für einen Zielordner (oder Datei) innerhalb einer Storage zurück.
Legt Ordner an, falls er noch nicht existiert
Beispiele:
\nn\t3::Storage()->getFolder( 'fileadmin/test/beispiel.txt' );
\nn\t3::Storage()->getFolder( 'fileadmin/test/' );
==> gibt \Folder-Object für den Ordner 'test/' zurück
Gibt den Folder-Object für einen Zielordner (oder Datei) innerhalb einer Storage zurück.
Legt Ordner an, falls er noch nicht existiert
Beispiele:
\nn\t3::Storage()->getFolder( 'fileadmin/test/beispiel.txt' );
\nn\t3::Storage()->getFolder( 'fileadmin/test/' );
==> gibt \Folder-Object für den Ordner 'test/' zurück
Den gesamten SysCategory-Baum (als Array) holen.
Jeder Knotenpunkt hat die Attribute 'parent' und 'children', um
rekursiv durch Baum iterieren zu können.
// Gesamten Baum holen
\nn\t3::SysCategory()->getTree();
// Bestimmten Ast des Baums holen
\nn\t3::SysCategory()->getTree( $uid );
// Alle Äste des Baums holen, key ist die UID der SysCategory
\nn\t3::SysCategory()->getTree( true );
Den gesamten SysCategory-Baum (als Array) holen.
Jeder Knotenpunkt hat die Attribute 'parent' und 'children', um
rekursiv durch Baum iterieren zu können.
// Gesamten Baum holen
\nn\t3::SysCategory()->getTree();
// Bestimmten Ast des Baums holen
\nn\t3::SysCategory()->getTree( $uid );
// Alle Äste des Baums holen, key ist die UID der SysCategory
\nn\t3::SysCategory()->getTree( true );
In den Seiteneigenschaften unter "Verhalten -> Enthält Erweiterung" eine Auswahl-Option hinzufügen.
Klassischerweise in Configuration/TCA/Overrides/pages.php genutzt, früher in ext_tables.php
// In ext_localconf.php das Icon registrieren (16 x 16 px SVG)
\nn\t3::Registry()->icon('icon-identifier', 'EXT:myext/Resources/Public/Icons/module.svg');
// In Configuration/TCA/Overrides/pages.php
\nn\t3::TCA()->addModuleOptionToPage('Beschreibung', 'identifier', 'icon-identifier');
Default Konfiguration für verschiedene, typische types im TCA holen.
Dient als eine Art Alias, um die häufigst verwendeten config-Arrays schneller
und kürzer schreiben zu können
Für jeden Typ lässt sich der am häufigsten überschriebene Wert im config-Array auch
per Übergabe eines fixen Wertes statt eines override-Arrays setzen:
Standard-Konfig inkl. Image-Cropper, Link und alternativer Bildtitel
Diese Einstellung ändert sich regelmäßig, was bei der Menge an Parametern
und deren wechselnden Position im Array eine ziemliche Zumutung ist.
Fügt Optionen aus TypoScript zur Auswahl in ein TCA ein.
Alias zu nnt3::Flexform->insertOptions( $config, $a = null );
Beschreibung und weitere Beispiele dort.
Eine Konfiguration des TCA überschreiben, z.B. um ein mask-Feld mit einem eigenen renderType zu
überschreiben oder Core-Einstellungen im TCA an den Tabellen pages oder tt_content zu ändern.
Folgendes Beispiel setzt/überschreibt im TCA das config-Array unter:
Siehe auch \nn\t3::TCA()->setContentConfig() für eine Kurzfassung dieser Methode, wenn es um
die Tabelle tt_content geht und \nn\t3::TCA()->setPagesConfig() für die Tabelle pages
In den Seiteneigenschaften unter "Verhalten -> Enthält Erweiterung" eine Auswahl-Option hinzufügen.
Klassischerweise in Configuration/TCA/Overrides/pages.php genutzt, früher in ext_tables.php
// In ext_localconf.php das Icon registrieren (16 x 16 px SVG)
\nn\t3::Registry()->icon('icon-identifier', 'EXT:myext/Resources/Public/Icons/module.svg');
// In Configuration/TCA/Overrides/pages.php
\nn\t3::TCA()->addModuleOptionToPage('Beschreibung', 'identifier', 'icon-identifier');
Default Konfiguration für verschiedene, typische types im TCA holen.
Dient als eine Art Alias, um die häufigst verwendeten config-Arrays schneller
und kürzer schreiben zu können
Für jeden Typ lässt sich der am häufigsten überschriebene Wert im config-Array auch
per Übergabe eines fixen Wertes statt eines override-Arrays setzen:
Standard-Konfig inkl. Image-Cropper, Link und alternativer Bildtitel
Diese Einstellung ändert sich regelmäßig, was bei der Menge an Parametern
und deren wechselnden Position im Array eine ziemliche Zumutung ist.
Fügt Optionen aus TypoScript zur Auswahl in ein TCA ein.
Alias zu nnt3::Flexform->insertOptions( $config, $a = null );
Beschreibung und weitere Beispiele dort.
Eine Konfiguration des TCA überschreiben, z.B. um ein mask-Feld mit einem eigenen renderType zu
überschreiben oder Core-Einstellungen im TCA an den Tabellen pages oder tt_content zu ändern.
Folgendes Beispiel setzt/überschreibt im TCA das config-Array unter:
Siehe auch \nn\t3::TCA()->setContentConfig() für eine Kurzfassung dieser Methode, wenn es um
die Tabelle tt_content geht und \nn\t3::TCA()->setPagesConfig() für die Tabelle pages
Vollständig initialisiertes TypoScript in den Request einschleusen.
Dies ist erforderlich, wenn in einem gecachten Frontend-Kontext ausgeführt wird,
in dem das TypoScript-Setup-Array nicht initialisiert ist. Es verwendet den
TypoScriptHelper, um ein vollständiges TypoScript-Objekt zu erstellen und es
in das frontend.typoscript-Attribut des Requests einzuschleusen.
// In der Middleware:
$request = \nn\t3::Tsfe()->injectTypoScript( $request );
"Softe" Variante: Nutzt ein fake USER_INT-Objekt, damit bereits gerenderte
Elemente nicht neu gerendert werden müssen. Workaround für TYPO3 v12+, da
TypoScript Setup & Constants nicht mehr initialisiert werden, wenn Seite
vollständig aus dem Cache geladen werden.
Vollständig initialisiertes TypoScript in den Request einschleusen.
Dies ist erforderlich, wenn in einem gecachten Frontend-Kontext ausgeführt wird,
in dem das TypoScript-Setup-Array nicht initialisiert ist. Es verwendet den
TypoScriptHelper, um ein vollständiges TypoScript-Objekt zu erstellen und es
in das frontend.typoscript-Attribut des Requests einzuschleusen.
// In der Middleware:
$request = \nn\t3::Tsfe()->injectTypoScript( $request );
"Softe" Variante: Nutzt ein fake USER_INT-Objekt, damit bereits gerenderte
Elemente nicht neu gerendert werden müssen. Workaround für TYPO3 v12+, da
TypoScript Setup & Constants nicht mehr initialisiert werden, wenn Seite
vollständig aus dem Cache geladen werden.
Nur Annotations holen, die in einem bestimmten Namespace sind.
In diesem Beispiel werden nur Annotations geholt, die mit @nn\rest
beginnen, z.B. @nn\rest\access ...
Nur Annotations holen, die in einem bestimmten Namespace sind.
In diesem Beispiel werden nur Annotations geholt, die mit @nn\rest
beginnen, z.B. @nn\rest\access ...
Diverse Methoden zum Parsen von PHP-Quelltexten und Kommentaren im
Quelltext (Annotations). Zielsetzung: Automatisierte Dokumentation aus den Kommentaren
im PHP-Code erstellen.
Beispiele für die Verwendung inkl. Rendering des Templates
Ähnelt parseFile() - allerdings muss hier der eigentliche Klassen-Name übergeben werden.
Wenn man nur den Pfad zur PHP-Datei kennt, nutzt man parseFile().
Parsed den Kommentar (Annotation) über der Klassen-Definition und optional auch alle Methoden der Klasse.
Gibt ein Array zurück, bei der auch die Argumente / Parameter jeder Methode aufgeführt werden.
Markdown kann in den Annotations verwendet werden, das Markdown wird automatisch in HTML-Code umgewandelt.
Einen Ordner (rekursiv) nach Klassen mit Annotations parsen.
Gibt ein Array mit Informationen zu jeder Klasse und seinen Methoden zurück.
Die Annotations (Kommentare) über den Klassen-Methoden können in Markdown formatiert werden, sie werden automatisch in HTML mit passenden <pre> und <code> Tags umgewandelt.
Ähnelt parseFile() - allerdings muss hier der eigentliche Klassen-Name übergeben werden.
Wenn man nur den Pfad zur PHP-Datei kennt, nutzt man parseFile().
Parsed den Kommentar (Annotation) über der Klassen-Definition und optional auch alle Methoden der Klasse.
Gibt ein Array zurück, bei der auch die Argumente / Parameter jeder Methode aufgeführt werden.
Markdown kann in den Annotations verwendet werden, das Markdown wird automatisch in HTML-Code umgewandelt.
Einen Ordner (rekursiv) nach Klassen mit Annotations parsen.
Gibt ein Array mit Informationen zu jeder Klasse und seinen Methoden zurück.
Die Annotations (Kommentare) über den Klassen-Methoden können in Markdown formatiert werden, sie werden automatisch in HTML mit passenden <pre> und <code> Tags umgewandelt.
Der Helper ermöglicht es, im TypoScript die JavaScript-Object-Schreibweise zu nutzen und über den {nnt3:parse.json()} ViewHelper in ein Array zu konvertieren.
Das ist praktisch, wenn z.B. Slider-Konfigurationen oder andere JavaScript-Objekte im TypoScript definiert werden sollen, um sie später in JavaScript zu nutzen.
Anderes Anwendungsbeispiel: Man möchte die "normalen" JS-Syntax in einer .json-Datei nutzen, statt dem JSON-Syntax.
Schauen wir uns ein Beispiel an. Dieser Text wurde in eine Textdatei geschrieben und soll per PHP geparsed werden:
// Inhalte einer Textdatei.
{
beispiel: ['eins', 'zwei', 'drei']
}
Copied!
PHP würde bei diesem Beispiel mit json_decode() einen Fehler melden: Der String enthält Kommentare, Umbrüche und die Keys und Values sind nicht in doppelte Anführungszeichen eingeschlossen. Der JsonHelper bzw. der ViewHelper $jsonHelper->decode() kann es aber problemlos umwandeln.
So könnte man im TypoScript Setup ein JS-Object definieren:
Die Mischung irritiert ein wenig: my_conf.data (...) öffnet im TypoScript einen Abschnitt für mehrzeiligen Code.
Zwischen den (...) steht dann ein "normales" JavaScript-Object.
Das lässt sich im Fluid-Template dann einfach als Array nutzen:
Dieses Script basiert überwiegend auf der Arbeit von https://bit.ly/3eZuNu2 und
wurde von uns für PHP 7+ optimiert.Alles an Ruhm und Ehre bitte in diese Richtung.
Die PHP-Funktion json_decode() funktioniert nur bei der JSON-Syntax: {"key":"value"}. Im JSON sind weder Zeilenumbrüche, noch Kommentare erlaubt.
Mit dieser Funktion können auch Strings in der JavaScript-Schreibweise geparsed werden.
Die PHP-Funktion json_decode() funktioniert nur bei der JSON-Syntax: {"key":"value"}. Im JSON sind weder Zeilenumbrüche, noch Kommentare erlaubt.
Mit dieser Funktion können auch Strings in der JavaScript-Schreibweise geparsed werden.
| @return array|string
Source Code
publicstaticfunctiondecode($str, $useArray=true){
$str = trim($str);
if(function_exists('json_decode'))
{
$json = json_decode($str,$useArray);
if($json !== null)
return $json;
}
$str = self::reduceString($str);
switch (strtolower($str)) {
case'true':
returntrue;
case'false':
returnfalse;
case'null':
returnnull;
default:
if (is_numeric($str)) {
// Lookie-loo, it's a number// This would work on its own, but I'm trying to be// good about returning integers where appropriate:// return (float)$str;// Return float or int, as appropriatereturn ((float)$str == (integer)$str)
? (integer)$str
: (float)$str;
} elseif (preg_match('/^("|\').+(\1)$/s', $str, $m) && $m[1] == $m[2]) {
// STRINGS RETURNED IN UTF-8 FORMAT
$delim = substr($str, 0, 1);
$chrs = substr($str, 1, -1);
$utf8 = '';
$strlen_chrs = strlen($chrs);
for ($c = 0; $c < $strlen_chrs; ++$c) {
$substr_chrs_c_2 = substr($chrs, $c, 2);
$ord_chrs_c = ord($chrs[$c]);
switch (true) {
case $substr_chrs_c_2 == '\b':
$utf8 .= chr(0x08);
++$c;
break;
case $substr_chrs_c_2 == '\t':
$utf8 .= chr(0x09);
++$c;
break;
case $substr_chrs_c_2 == '\n':
$utf8 .= chr(0x0A);
++$c;
break;
case $substr_chrs_c_2 == '\f':
$utf8 .= chr(0x0C);
++$c;
break;
case $substr_chrs_c_2 == '\r':
$utf8 .= chr(0x0D);
++$c;
break;
case $substr_chrs_c_2 == '\\"':
case $substr_chrs_c_2 == '\\\'':
case $substr_chrs_c_2 == '\\\\':
case $substr_chrs_c_2 == '\\/':
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
$utf8 .= $chrs[++$c];
}
break;
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
// single, escaped unicode character
$utf16 = chr(hexdec(substr($chrs, ($c+2), 2)))
. chr(hexdec(substr($chrs, ($c+4), 2)));
$utf8 .= self::utf16beToUTF8($utf16);
$c+=5;
break;
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
$utf8 .= $chrs[$c];
break;
case ($ord_chrs_c & 0xE0) == 0xC0:
// characters U-00000080 - U-000007FF, mask 110XXXXX//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 2);
++$c;
break;
case ($ord_chrs_c & 0xF0) == 0xE0:
// characters U-00000800 - U-0000FFFF, mask 1110XXXX// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 3);
$c += 2;
break;
case ($ord_chrs_c & 0xF8) == 0xF0:
// characters U-00010000 - U-001FFFFF, mask 11110XXX// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 4);
$c += 3;
break;
case ($ord_chrs_c & 0xFC) == 0xF8:
// characters U-00200000 - U-03FFFFFF, mask 111110XX// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 5);
$c += 4;
break;
case ($ord_chrs_c & 0xFE) == 0xFC:
// characters U-04000000 - U-7FFFFFFF, mask 1111110X// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 6);
$c += 5;
break;
}
}
return $utf8;
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
// array, or object notationif ($str[0] == '[') {
$stk = array(self::JSON_IN_ARR);
$arr = array();
} else {
if ($useArray) {
$stk = array(self::JSON_IN_OBJ);
$obj = array();
} else {
$stk = array(self::JSON_IN_OBJ);
$obj = new stdClass();
}
}
$stk[] = array('what' => self::JSON_SLICE, 'where' => 0, 'delim' => false);
$chrs = substr($str, 1, -1);
$chrs = self::reduceString($chrs);
if ($chrs == '') {
if (reset($stk) == self::JSON_IN_ARR) {
return $arr;
} else {
return $obj;
}
}
//print("\nparsing [$chrs}\n");
$strlen_chrs = strlen($chrs);
for ($c = 0; $c <= $strlen_chrs; ++$c) {
$top = end($stk);
$substr_chrs_c_2 = substr($chrs, $c, 2);
if (($c == $strlen_chrs) || (($chrs[$c] == ',') && ($top['what'] == self::JSON_SLICE))) {
// found a comma that is not inside a string, array, etc.,// OR we've reached the end of the character list
$slice = substr($chrs, $top['where'], ($c - $top['where']));
$stk[] = array('what' => self::JSON_SLICE, 'where' => ($c + 1), 'delim' => false);
//print("Found split at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");if (reset($stk) == self::JSON_IN_ARR) {
// we are in an array, so just push an element onto the stack
$val = self::decode($slice,$useArray);
if (is_string($val)) $val = html_entity_decode($val);
$arr[] = $val;
} elseif (reset($stk) == self::JSON_IN_OBJ) {
// we are in an object, so figure// out the property name and set an// element in an associative array,// for nowif (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// "name":value pair
$key = self::decode($parts[1],$useArray);
$val = self::decode($parts[2],$useArray);
if (is_string($val)) $val = html_entity_decode($val);
if ($useArray) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// name:value pair, where name is unquoted
$key = $parts[1];
$val = self::decode($parts[2],$useArray);
if (is_string($val)) $val = html_entity_decode($val);
if ($useArray) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
}
}
} elseif ((($chrs[$c] == '"') || ($chrs[$c] == "'")) && ($top['what'] != self::JSON_IN_STR)) {
// found a quote, and we are not inside a string
$stk[] = array('what' => self::JSON_IN_STR, 'where' => $c, 'delim' => $chrs[$c]);
//print("Found start of string at [$c]\n");
} elseif (($chrs[$c] == $top['delim']) &&
($top['what'] == self::JSON_IN_STR) &&
(($chrs[$c - 1] != "\\") ||
($chrs[$c - 1] == "\\" && $chrs[$c - 2] == "\\"))) {
// found a quote, we're in a string, and it's not escaped
array_pop($stk);
//print("Found end of string at [$c]: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
} elseif (($chrs[$c] == '[') &&
in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) {
// found a left-bracket, and we are in an array, object, or slice
$stk[] = array('what' => self::JSON_IN_ARR, 'where' => $c, 'delim' => false);
//print("Found start of array at [$c]\n");
} elseif (($chrs[$c] == ']') && ($top['what'] == self::JSON_IN_ARR)) {
// found a right-bracket, and we're in an array
array_pop($stk);
//print("Found end of array at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} elseif (($chrs[$c] == '{') &&
in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) {
// found a left-brace, and we are in an array, object, or slice
$stk[] = array('what' => self::JSON_IN_OBJ, 'where' => $c, 'delim' => false);
//print("Found start of object at [$c]\n");
} elseif (($chrs[$c] == '}') && ($top['what'] == self::JSON_IN_OBJ)) {
// found a right-brace, and we're in an object
array_pop($stk);
//print("Found end of object at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
} elseif (($substr_chrs_c_2 == '/*') &&
in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) {
// found a comment start, and we are in an array, object, or slice
$stk[] = array('what' => self::JSON_IN_CMT, 'where' => $c, 'delim' => false);
$c++;
//print("Found start of comment at [$c]\n");
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == self::JSON_IN_CMT)) {
// found a comment end, and we're in one now
array_pop($stk);
$c++;
for ($i = $top['where']; $i <= $c; ++$i)
$chrs = substr_replace($chrs, ' ', $i, 1);
//print("Found end of comment at [$c]: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
}
}
if (reset($stk) == self::JSON_IN_ARR) {
return $arr;
} elseif (reset($stk) == self::JSON_IN_OBJ) {
return $obj;
}
}
}
}
Copied!
JsonHelper::encode()
\nn\t3::JsonHelper()->encode($var);
Konvertiert eine Variable ins JSON Format.
Relikt der ursprünglichen Klasse, vermutlich aus einer Zeit als es json_encode() noch nicht gab.
Damit diese Funktion nutzbar ist, muss im Extension-Manager von nnhelpers ein Deep-L Api-Key hinterlegt werden.
Der Key ist ist kostenfrei und erlaubt die Übersetzung von 500.000 Zeichen pro Monat.
// Übersetzer aktivieren
$translationHelper = \nn\t3::injectClass( \Nng\Nnhelpers\Helpers\TranslationHelper::class );
// Übersetzung per Deep-L erlauben
$translationHelper->setEnableApi( true );
// Zielsprache festlegen
$translationHelper->setTargetLanguage( 'EN' );
// Max. Anzahl der Übersetzungen erlauben (zwecks Debugging)
$translationHelper->setMaxTranslations( 2 );
// Pfad, in dem die l18n-Dateien gespeichert / gecached werden sollen
$translationHelper->setL18nFolderpath( 'EXT:nnhelpers/Resources/Private/Language/' );
// Übersetzung starten
$text = $translationHelper->translate('my.example.key', 'Das ist der Text, der übersetzt werden soll');
Erzeugt einen eindeutigen Hash / Checksum aus dem Text.
Der übergebene Text ist immer die Basis-Sprache. Ändert sich der Text in der Basissprache, gibt die Methode eine andere Checksum zurück.
Dadurch wird erkannt, wann ein Text neu übersetzt werden muss. Reine Änderungen an Whitespaces und Tags werden ignoriert.
Setzt den aktuellen Ordner, in dem die Übersetzungs-Dateien gecached werden.
Idee ist es, die übersetzten Texte für Backend-Module nur 1x zu übersetzen und dann in dem Extension-Ordner zu speichern.
Von dort werden sie dann ins GIT deployed.
Setzt die maximale Anzahl an Übersetzungen, die pro Instanz gemacht werden sollen.
Hilft beim Debuggen (damit das Deep-L Kontingent nicht durch Testings ausgeschöpft wird) und bei TimeOuts, wenn viele Texte übersetzt werden müssen.
$translationHelper->setMaxTranslations( 5 ); // Nach 5 Übersetzungen abbrechen
Erzeugt einen eindeutigen Hash / Checksum aus dem Text.
Der übergebene Text ist immer die Basis-Sprache. Ändert sich der Text in der Basissprache, gibt die Methode eine andere Checksum zurück.
Dadurch wird erkannt, wann ein Text neu übersetzt werden muss. Reine Änderungen an Whitespaces und Tags werden ignoriert.
Setzt den aktuellen Ordner, in dem die Übersetzungs-Dateien gecached werden.
Idee ist es, die übersetzten Texte für Backend-Module nur 1x zu übersetzen und dann in dem Extension-Ordner zu speichern.
Von dort werden sie dann ins GIT deployed.
Setzt die maximale Anzahl an Übersetzungen, die pro Instanz gemacht werden sollen.
Hilft beim Debuggen (damit das Deep-L Kontingent nicht durch Testings ausgeschöpft wird) und bei TimeOuts, wenn viele Texte übersetzt werden müssen.
$translationHelper->setMaxTranslations( 5 ); // Nach 5 Übersetzungen abbrechen
Get complete TypoScript setup for a given page ID following TYPO3 13 approach.
$typoScriptHelper = \nn\t3::injectClass( \Nng\Nnhelpers\Helpers\TypoScriptHelper::class );
// get typoscript for current page
$typoScriptHelper->getTypoScript();
// get typoscript for page with uid 1
$typoScriptHelper->getTypoScript( 1 );
Copied!
@param int $pageUid Page UID to get TypoScript for
@return array Complete TypoScript setup with dot-syntax
Get complete TypoScript setup for a given page ID following TYPO3 13 approach.
$typoScriptHelper = \nn\t3::injectClass( \Nng\Nnhelpers\Helpers\TypoScriptHelper::class );
// get typoscript for current page
$typoScriptHelper->getTypoScript();
// get typoscript for page with uid 1
$typoScriptHelper->getTypoScript( 1 );
Copied!
@param int $pageUid Page UID to get TypoScript for
@return array Complete TypoScript setup with dot-syntax
Lädt die rohen Daten einer Spalte (colPos) des Backend Layouts.
Es handelt sich hier um das rohe tt_content-data-Array einer Spalte (colPos) aus dem Backend-Layout.
Per default werden auch die Relationen (FAL, assets, media...) geladen. Kann per relations:0 verhindert werden.
Variablen im gerenderten Content-Element ersetzen.
Erlaubt es, im Backend ein Inhaltselement anzulegen, das mit Fluid-Variablen arbeitet – z.B. für ein Mail-Template, bei dem der Empfänger-Name im Text erscheinen soll.
Hilfreich, falls z.B. eine Mail versendet werden soll mit Bestätigungs-Link.
Die UID des Datensatzes wird zusätzlich als Hash übergeben. Im Controller wird dann überprüft,
ob aus der übergeben uid der übergeben hash generiert werden kann.
Falls nicht, wurde die uid manipuliert.
Der Code kann über download zum direkten Download verfügbar gemacht werden.
Die Datei wird dabei dynamisch per JS generiert und gestreamt – es enstehen keine zusätzlichen Dateien auf dem Server.
nnhelpers nutzt diese Funktion, um die Boilerplates als Download anzubieten.
<nnt3:format.htmlToSphinx>
<p>Das ist eine Beschreibung dieser Methode</p>
<pre><code>$a = 99;</code></pre>
</nnt3:format.htmlToSphinx>
Copied!
wird das hier gerendert:
Das ist eine Beschreibung dieser Methode
.. code-block:: php
$a = 99;
Copied!
| @return string
format.indentCode
Description
<nnt3:format.indentCode />
Fügt eine Einrückung (Indentation) zu jeder Zeile eines Code-Blocks hinzu.
Wird für die Sphinx-Dokumentation verwendet, um Code innerhalb von .. code-block:: korrekt einzurücken.
{sourceCode->nnt3:format.indentCode(spaces: 3)}
Copied!
| @return string
format.jsonEncode
Description
<nnt3:format.jsonEncode />
Wandelt ein Array oder Object ins JSON-Format um.
{some.object->nnt3:format.jsonEncode()}
Copied!
| @return string
format.replace
Description
<nnt3:format.replace />
Text in einem String suchen und ersetzen.
{nnt3:format.replace(str:'alles schön im Juli.', from:'Juli', to:'Mai')}
{varname->nnt3:format.replace(from:'Juli', to:'Mai')}
Copied!
| @return string
frontendUser.get
Description
<nnt3:frontendUser.get />
Frontend-User holen
Gibt ein Array mit den Daten des Frontend-Users zurück, z.B. um Seiten, Mails oder Inhalte zu personalisieren.
Wirft keinen Fehler, falls kein image oder src übergeben wurde.
Erlaubt auch die Übergabe eines Arrays, zieht sich einfach das erste Bild.
// tt_content.image ist eigentlich ein Array. Es wird einfach das erste Bild gerendert!
{nnt3:image(image:data.image)}
// wirft keinen Fehler (falls der Redakteur kein Bild hochgeladen hat!)
{nnt3:image(image:'')}
Copied!
link.cloneRecord
Description
<nnt3:link.cloneRecord />
Link zum Klonen eines Datensatzes für ein Backend-Modul generieren.
Wichtig, damit slide funktioniert: Falls die Tabelle pages um ein eigenes Feld erweitert wurde, muss das Feld vorher in der ext_localconf.php registriert werden.
\nn\t3::Registry()->rootLineFields(['logo']);
Copied!
page.title
Description
<nnt3:page.title />
Aktuelle Page-Title setzen.
Andert das <title>-Tag der aktuellen Seite.
Funktioniert nicht, wenn EXT:advancedtitle aktiviert ist!
Wandelt ein normales JavaScript-Object, dass als String übergeben wird in ein Array um.
Erlaubt es, Konfigurationen für Slider und andere JS-Bibliotheken im TypoScript anzulegen und später per JS zu parsen.
Hilfreich z.B. für eindeutige IDs oder Klassen-Namen in Fluid-Templates.
{nnt3:uniqid()}
Copied!
<div id="box-{nnt3:uniqid()}"> ... </div>
Copied!
| @return string
uri.image
Description
<nnt3:uri.image />
Vereinfacht die Verwendung des Uri.ImageViewhelpers.
Wirft keinen Fehler, falls kein image oder src übergeben wurde.
Erlaubt auch die Übergabe eines Arrays, zieht sich einfach das erste Bild.
// tt_content.image ist eigentlich ein Array. Es wird einfach das erste Bild gerendert!
{nnt3:uri.image(image:data.image)}
// wirft keinen Fehler (falls der Redakteur kein Bild hochgeladen hat!)
{nnt3:uri.image(image:'')}
Copied!
uri.page
Description
<nnt3:uri.page />
Erzeugt ein URL zu einer Seite im Frontend.
Entspricht fast exakt dem Typo3 ViewHelper {f:uri.page()} - kann allerdings auch in einem Kontext
verwendet werden, bei dem kein Frontend (TSFE) existiert, z.B. im Template eines Backend-Moduls oder in
Mail-Templates eines Scheduler-Jobs.
Du denkst vielleicht: Die Typo3 DebuggerUtility ist großartig. Ja, ist sie.
Aber: Hast Du jemals versucht, einen QueryBuilder zu debuggen, weil Du mit dem "Old School" Blick auf ein SQL-Statement besser verstehst, was genau bei Deiner Datenbankabfrage schief geht? Hast Du Dich dann gewundert, dass Du keine Paramter siehst, sondern nur ? an den entscheidenden Stellen? Wie lange hast Du gegoogelt, um eine Antwort zu finden und Dich dann wieder einmal gefragt: "Muss das so kompliziert sein?"
Ist es Dir schon mal passiert, dass Du ein debug() im Code vergessen hast und am nächsten Morgen einfach nicht mehr wusstest, wo Du diesen debug hingeschrieben hast? Wäre es nicht schön, einfach die Zeilennummer und das Script direkt sehen zu können, die das debug getriggert hat?
Wenn Du auch sonst keine einzige anderer Methode aus nnhelpers anfassen wirst:
Hier ist die eine Zeile, nach der Du süchtig werden wirst:
\nn\t3::debug( $whatever );
Copied!
Extensions entwickeln
Wenn man Jahre lang Extensions für Typo3 entwickelt, freut man sich über vieles. Aber man wundert sich auch oft.
Eine davon: Seit einigen Versionen, kann man Icons für das Backend (z.B. für ein Backend-Modul) nicht mehr als Pfad zum svg oder jpg angeben. Stattdessen geht man den Umweg über die IconRegistry und registriert das Icon in der ext_tables.php.
Mag Sinn haben. Aber es wird ja noch komplizierter. Abhängig vom Dateityp muss man zunächst den passenden IconProvider dazu instanziieren. Für ein svg landet man so bei einigen Zeilen Code – vom SvgIconProvider bis zum IconRegistry.
Das ist eine Menge Hirnschmalz dafür, dass doch eigentlich das Suffix .svg sagen sollte, um welchen Dateityp es sich handelt.
Wir finden, Helpers könnte uns diese Denkarbeit abnehmen:
Noch einer unserer Lieblinge: Im TCA ein Feld für eine FAL Relation definieren. Erinnerst Du Dich an die 28 Zeilen code, um einen Dateiupload im Backend zu ermöglichen? Hast Du gemerkt, dass sich die Struktur gerne von Version zu Version ändert – und musstest Du durch alle TCAs gehen und die Anpassungen nachziehen?
Hier ist ein magischer Einzeiler für Dein nächstes FAL:
Und natürlich gibt es noch viele, weitere Einzeiler, z.B. für die Definition eines Texteditors (ckeditor).
Das beste daran: Wenn das Core-Team sich entscheidet, von ckeditor Abschied zu nehmen (so wie sie es - zum Glück - vor einigen Jahren mit der rtehtmlarea getan haben), dann wird das nicht mehr Dein Problem sein. Lass es das Problem von nnhelpers sein.
Lass uns durchdrehen. Schon mal darüber nachgedacht, ein externes FlexForm in ein TCA-Feld einzuschleusen? Im Grunde macht Typo3 das ja bei jedem Plugin. Und DCE definiert über FlexForms den gesamten Inhalt des DCE-Elementes.
Nicht träumen. Coden. Und – klar – natürlich mal wieder mit einem einzigen Einzeiler.
Klingt vielleicht verrückt, aber tatsächlich nutzen wir diese Methode ständig – vor allem im Kontext mit der besten Extension, die je für Typo3 erfunden wurde: Mask (EXT:mask).
Im folgenden Beispiel hatten wir ca. 30 Slider-Optionen für Übergänge, Dauer, Breakpoints / Responsivität und vieles mehr. Jede Option sollte in dem Mask-Element vom Redakteur auswählbar sein. Mit den Standard-Feldern von EXT:mask hätten wir die Datenbank-Tabelle tt_content um 30 Felder erweitern müssen. Felder, an die keine andere Logik gebunden ist – keine Indizierung, Sortierung oder Suche. So ein Fall schreit förmlich nach einem FlexForm statt einzelnen Datenbank-Feldern.
Mask selbst erlaubt (noch) kein FlexForm. Aber, was viele nicht wissen: Felder lassen sich in der Configuration/TCA/Overrides/tt_content.php seiner eigenen Extension einfach neu konfigurieren. (Achtung, Stolperfalle: Die eigene Extension muss eine Dependency zu mask in der ext_emconf.php definiert haben!)
Das ganze sieht dann so aus:
if ($_GET['route'] != '/module/tools/MaskMask') {
if ($GLOBALS['TCA']['tt_content']['columns']['tx_mask_slideropt']) {
$GLOBALS['TCA']['tt_content']['columns']['tx_mask_slideropt']['config'] = \nn\t3::TCA()->insertFlexForm('FILE:EXT:myext/Configuration/FlexForm/customFlexForm.xml');
}
}
Copied!
FlexForm pimpen
In einem Plugin (FlexForm) möchte man häufig dem Redakteur die Möglichkeit bieten, zwischen verschiendenen Layouts, Farben oder Designs zu wählen. Dazu nutzt man üblicherweise eine select / selectSingle Definition mit allen Optionen.
Praktischer finde ich es persönlich, wenn sich die Optionen, die im Dropdown erscheinen, per TypoScript-Setup oder PageTSconfig definieren lassen. Pro "Ast" im Seitenbaum können so unterschiedliche Optionen zur Verfügung gestellt werden. Außerdem finde ich es "praktischer" nicht jedesmal, wenn eine Option dazukommt, im XML des FlexForms zu hantieren.
Hier ist ein schöner, kleiner Helfer dazu:
<config><type>select</type><renderType>selectSingle</renderType><itemstype="array"></items><itemsProcFunc>nn\t3\Flexform->insertOptions</itemsProcFunc><typoscriptPath>plugin.tx_extname.settings.colors</typoscriptPath><!-- Alternativ: Load options from the PageTSConfig: --><pageconfigPath>tx_extname.colors</pageconfigPath><insertEmpty>1</insertEmpty></config>
Copied!
Stimmt – und dann ist da noch die Sache mit der Länderauswahl im FlexForm. Warum auch hier komplizierter werden, als unbedingt nötig?
Großartig. Typo3 hat sich gerade von SwiftMailer verabschiedet.
Erinnerst Du Dich? Das hatten wir vor ein paar Jahren schon mal – als Typo3 den Schritt ZU SwiftMailer gemacht hat. Blöd, wenn man mail in 562 Extensions genutzt hat.
Ach, und: Sorry, nein. Wir haben vor dem letzten großen Typo3 Update nicht Stunden damit verbracht, uns durch alle breaking changes zu wühlen. Wenn wir ehrlich sind, läuft ein Update auf eine neue LTS immer so ab: Helm auf, anschnallen und auf den ersten Knall warten. Google wird uns schon irgendwie aus der Unfallstelle rausschneiden.
Gut zu wissen, dass sich ab sofort ein paar Dinge **nie wieder*+ ändern werden.
Angst vor Outlook? Nichts mehr zu befürchten. Der Mailhelper hat einen kleinen, weitere Gehilfen am Start: Emogrifier. Der lässt sich auch mit einer Option abschalten – aber wozu... er beantwortet vielleicht eine Frage, die Du noch gar nicht gestellt hattest.
| Schön – aber hattest Du nicht letztes Mal das Problem, dass Du die Anhänge dynamisch während des Rendering Deines Mail-Templates einbinden wolltest? Kopf zerbrochen? Wie wäre es hiermit gewesen? Ein data-embed="1" in Deinem Template und Dein neuer, bester Freund macht die Denkarbeit für Dich.
In dem Beispiel oben haben wir noch gar nicht über den StandaloneView zum Rendern von Templates gesprochen. Klar, macht nnhelpers Dir auch hier das Leben leichter:
Da war noch die Sache mit den partialRootPaths, layoutRootPaths etc. Aber mal ehrlich: Ist man denn nicht (fast) immer in einer Extension, die auch die Templates dazu im Standard-Template-Ordner dieser Extension hat?
Die Beispiel zeigen, was nnhelpers besonders macht. Nicht strict sondern smart ist hier der Ansatz. Wenn Du einfach das tust, was Du intuitiv tun würdest, dann kannst Du relativ sicher sein: Genau so haben wir bei der Entwicklung von nnhelpers auch gedacht.
Daten von einer Extension migrieren
Wir haben kürzlich ein großes Projekt von Typo3 7 LTS auf Typo3 10 LTS aktualisiert. Das Projekt hatte Calendar Base (EXT:cal) im Einsatz und stolze 5.000 Kalendereinträge. Leider wurde EXT:cal (noch) nicht für Typo3 10 aktualisiert, so dass wir uns entschlossen, auf unsere eigene Kalendererweiterung nncalendar umzusteigen (die in ein paar Wochen im TER veröffentlicht wird).
Wir standen vor drei großen Herausforderungen:
Es war unmöglich, EXT:cal in Typo3 10 zu aktivieren - folglich gab es keine einfache Möglichkeit, auf die Datenbank-Tabellen von Calendar Base zuzugreifen oder "schöne" Modelle mit Gettern und Settern zu erstellen
Calendar Base hatte in Version 7 noch eine eigene Kategorisierung und verwendete noch keine sys_category.
Es gab tonnenweise Bilder im Ordner uploads/pics/, die in FAL-Bilder umgewandelt und an das neue EntryModel von nncalendar angehängt werden mussten
Das ist das destillerte Ergebnis:
// Holt alle Datensätze von EXT:cal. Die Ext muss dazu nicht aktiviert sein!
$calData = \nn\t3::Db()->statement( "SELECT * FROM tx_cal_event WHERE deleted = 0");
// Das neue Repo für die Kalender-Einträge
$calendarRepository = \nn\t3::injectClass(\Nng\Nncalendar\Domain\Repository\EntryRepository::class);
// NnCalendar-Modelle aus den rohen array-Daten erzeugen!foreach ($calData as $row) {
// [...] hier gab es noch ein paar Zeilen Code, um Datum etc. zu parsen.
$entry = \nn\t3::Convert($row)->toModel( \Nng\Nncalendar\Domain\Model\Entry::class );
$calendarRepository->add( $entry );
}
\nn\t3::Db()->persistAll();
Copied!
Auch die neuen SysCategories zu setzen war so einfach:
nnhelpers erkennt automatisch, dass das Entry-Model eine Relation zu den SysCategories im Feld category hat und erzeugt die ObjectStorage mit den SysCategories automatisch.
Bei den Bildern wurde es auch nicht komplizierter:
Auch hier erkennt nnhelpers automatisch, dass die Property falImage ein FAL or eine ObjectStorage im Entry-Model möchte und erzeugt die sys_file und sys_file_reference automatisch.
Fein. Und was machst Du jetzt den Rest des Tages?
Database operations
Ich kann mich nicht erinnern, wie oft wir einfach nur ein direktes und unkompliziertes Update, Löschen oder Einfügen von einzelnen Datensätzen in einer Datenbanktabelle durchführen wollten.
Doctrine ist eine der genialsten Kreationen der vergangenen Jahre, aber es gibt Anwendungsfälle, bei denen man einfach einfach bleiben möchte. Und was ist einfacher, als ein Einzeiler – der im Hintergrund daraus eine "ganz normale" Doctrine Query baut.
Hier ist ein kleiner Auszug aus den nn\t3::Db()-Methoden, die uns jeden Tag Zeit sparen:
Du glaubst noch immer nicht, dass diese kleine Extension Deinen Typo3-Alltag auf den Kopf stellen könnte?
Werfen wir einen Blick auf einige klassische Aufgaben. Die linke Spalte wirst Du gut kennen: So arbeitest Du bisher. Rechts daneben siehst Du die Lösung mit nnhelpers.
Vergleiche nicht nur die Anzahl der Code-Zeilen, die für das Lösen der Aufgabe erforderlich sind - sondern auch den geistigen Energieaufwand, den man braucht, um sich
die Schritte, Methoden und Parameter zu merken.
Achten auch darauf, wie sich das z.B. Grundkonzept beim Erstellen von Links im Backend-Kontext von Version 8 zu 9 verändert hat.
Ja, die Situation hat sich deutlich verbessert. Aber denke mal an die Zeit, die Du wieder gebraucht hättest, diese Lösung zu finden und sie in allen Deinen Extensions zu aktualisieren!
Dann sieh Dir den nnhelpers-Einzeiler auf der rechten Seite an. Siehst Du den Unterschied im Aufbau von Links im Frontend- oder Backend-Kontext?
Und wie steht es mit dem Sprung von Version 8 LTS zu 9 LTS?
Ich denke, Du verstehst, um was es geht.
Ein TypoScript-Setup außerhalb des Controllers erhalten
Aufgabe: Du möchtest den Wert für demo.path aus den TypoScript-Settings eines Plugins holen, aber leider bist Du nicht in einem Kontext, bei dem $this->settings vorhanden ist.
Vielleicht bist Du in einem Repository – oder ViewHelper.
Aufgabe: Einen Link zu einer Seite im Frontend erzeugen während Du im Frontend-Context bist.
Dieses Script könnte z.B. in einem Controller oder ViewHelper sein.
Einen Link bauen - im Backend Context, vor Version 9 LTS
Aufgabe: Einen Link zu einer Seite im Frontend bauen, allerdings im Backend- oder CLI-Context.
Sprich: Du hast kein Frontend, brauchst aber einen Link zu einer Seite im Frontend. Auf dieses Problem stößt jeder, der mal
versucht hat, innerhalb eines eigenen Scheduler-Tasks einen Link zu generieren.
Bis Version 9 musste man dazu das Frontend manuell instanziieren, was ungefähr in diesem Wahnsinn endete:
Aufgabe: Alle Daten einer Tabelle aus der Datenbank lesen, aber die Flags für hidden sowie die start_time und end_time ignorieren.
Diese Anwendung hat man häufig, wenn man extrem viele Datensätze möglichst performant und ohne Model braucht, z.B. für einen Eport nach Excel.
First public release after many years of development and improvement.
Bekannte Probleme
Leider massenhafte Problem in Verbindung mit der großen, roten Warnung auf dieser Seite
Helpers für WordPress
Wir haben eine Vision.
Was wäre, wenn es eine ähnliche Sammlung an "Helfern" für andere Content-Management-Systeme gäbe? Wenn die Methoden sogar
(fast) deckungsgleich wären? Wenn man als Entwickler zwischen Joomla, WordPress, Drupal, NodeJS und TYPO3 einfach
"springen" könnte, ohne sich immer wieder in andere Konzepte und Core-APIs einzuarbeiten.
Das ist die Idee hinter nnhelpers – mit dem langfristigen Ziel, sogar einen Großteil des Codes zwischen unterschiedlichen
CMS wiederverwendbar zu machen. Klar: Das ist ein Traum und er ist mit Hürden verbunden. Aber allein zu wissen: Es gibt
\nn\t3::debug() oder \nn\t3::Db()->findAll() oder \nn\t3::Environment()->getBaseUrl() – und dieser Befehl ist
Framework-übergreifend gleich wäre schon eine große Hilfe. Egal, ob man nnhelpers dann wirklich nutzen will – oder einfach
nur als "Spickzettel" verwendet, um zu sehen, wie die Funktion im Detail innerhalb des jeweiligen Systems implementiert wird.
Wir haben in 2022 einen Startpunkt gesetzt und angefangen, wphelpers ins Leben zu rufen: Eine Spiegelung von nnhelpers
für WordPress!
TYPO3Fluid als Rendering Engine in WordPress nutzen
Was war einer unserer ersten Schritte und Methoden der wphelpers? Eine vernünftige Template-Engine an den Start bringen.
WordPress setzt ja bekanntlich auf PHP-Templating – aus Sicht eines Fluid- oder Twig-verwöhnten Entwicklers eher eine
anachronistische Vollkatastrophe.
Mit wphelpers kann man jetzt innerhalb seines WordPress-Plugins jetzt diese schöne Zeile nutzen:
... und damit ein Fluid-Template rendern! Dank der Community, die Fluid als Standalone Version
bereit gestellt hat. Und da Fluid immer noch eine der besten Template-Engines ist – warum nicht WordPress damit "upgraden".
Damit sind schon mal alle Templates der TYPO3-Extensions in WordPress wiederverwendbar.
Und die Performance? WordPress argumentiert immer, dass nichts performanter als ein PHP-Template ist. Aber wer Fluid kennt,
der weiß, dass alle Templates (wie auch bei Smarty etc.) in reinen PHP-Code "übersetzt" und gecached werden. Es wird also
faktisch kaum einen Unterschied in der Performance geben.
Let's do it!
Wenn es dort draußen noch andere Teams gibt, die sich in den Paralleluniversen zwischen TYPO3, WordPress etc. bewegen und
diese Idee interessant finden: Was haltet ihr davon? Lust, mit einzusteigen? Lust eine Methode auf nnhelpers in
ein anderes System zu übersetzen? Let's start the revolution ;)
Wir freuen uns auf Euer Feedback!
Sitemap
Reference to the headline
Copy and freely share the link
This link target has no permanent anchor assigned.The link below can be used, but is prone to change if the page gets moved.