This extension will save you much, much time. Time that you will have to spend on other things. With your children, your wife or your mother-in-law. In other words: You might run out of excuses.
If you have any doubts whether you can cope with this, you should NOT install nnhelpers. We are not responsible for any side effects that may occur in your developer life as a result of the time gained.
What does it do?
Let it (nn)help you, whenever things seem to get more compliated that they should be.
Let's look at a few examples:
Let's say you have built an upload form. The user can upload an image in the frontend. You would like to have the image as FAL on a model.
Sounds like an everyday task, doesn't it? So: Let's ask Google. After countless variations of search phrases, you land on a promising doc at docs.typo3.org.
You work your way through many lines of information and end up with this statement: Not our business. Take care of it yourself, or steal it from Helmut. You then spend then the next 30 minutes with lovely Helmut. Enjoying beautiful source codes. And searching desperately for the little snippet you need for your project.
I don't know how long it took you to solve this task the first time. For us it was about four hours. We were in Typo3 version 6 at that time. When the 7 LTS was released, we searched again because some things changed in the core. When version 8 LTS was released, it was again almost 2 hours. And then the adaption had to be done over and over again for ALL our extensions that implemented a file upload from the frontend context.
This is a nightmare and time that seems kind of pointless. I prefer to spend this time - without batting an eye - with my choleric mother-in-law.
How would we solve this task today?
We would ask nnhelpers for help.
So let's switch to the backend module of nnhelpers and have a look:
The backend module shows all methods nicely grouped by topics.
What were we looking for?
Right. Something related to FAL.
So let's use the search function or scroll down to the FAL section.
Every method has detailed examples and use-cases.
setInModel() sounds pretty much like the thing we're looking for.
Lets look at an the examples.
Now wait, are you really telling me, all I need is this f..g line of code?
WTF? No ObjectManager->get(), no @inject? Just a no-brainer ONELINER?
And the best part is: This onliner won't ever change.
Not for Typo3 Version 7. Not for Typo3 11. We promise.
Yeah, but that is breaking the rules I learned at the university!! This is ugly!!
Right. But if it is simple, we simply don't care.
This is a line of code you can remember. And if not: next time you will know, where to find it.
You still don't like it?
Fine, then have a look at the source code and steal, what you need to build it by yourself. No need to leave the backend and dive into the source code of nnhelpers. It's all here where it should be.
Every method has detailed examples and use-cases.
Ok, it's your decision. ;)
Got it? Stop thinking, start coding.
Installation
Nothing really special about the installation. Simply do, what you always do to get the extension up and running.
No need to add any TypoScript Templates.
You love the Extension Manager?
Press the Retrieve/Update button and search for the extension key nnhelpers and import the extension from the repository. Start coding. Have fun.
Methods to check in the frontend whether a user is logged into the Typo3 backend and has admin rights, for example.
Methods to start a backend user if it does not exist (e.g. during a scheduler job).
Overview of Methods
\nn\t3::BackendUser()->get();
Gets the current backend user.
Corresponds to $GLOBALS['BE_USER'] in previous Typo3 versions.
Checks whether a BE user is logged in.
Example: Only show certain content in the frontend if the user is logged in in the backend.
Previously: $GLOBALS['TSFE']->beUserLogin
// Check after complete initialization of the front/backend
\nn\t3::BackendUser()->isLoggedIn();
// Check using the JWT, e.g. in an eID script before authentication
\nn\t3::BackendUser()->isLoggedIn( $request );
Start (fake) backend user.
Solves the problem that, for example, certain functions from the scheduler
such as log() are not possible if there is no active BE user.
Saves user-specific settings for the currently logged in backend user.
These settings are also available again for the user after logout/login.
See \nn\t3::BackendUser()->getSettings('myext') to read the data.
Checks whether a BE user is logged in.
Example: Only show certain content in the frontend if the user is logged in in the backend.
Previously: $GLOBALS['TSFE']->beUserLogin
// Check after complete initialization of the front/backend
\nn\t3::BackendUser()->isLoggedIn();
// Check using the JWT, e.g. in an eID script before authentication
\nn\t3::BackendUser()->isLoggedIn( $request );
Start (fake) backend user.
Solves the problem that, for example, certain functions from the scheduler
such as log() are not possible if there is no active BE user.
Saves user-specific settings for the currently logged in backend user.
These settings are also available again for the user after logout/login.
See \nn\t3::BackendUser()->getSettings('myext') to read the data.
Methods for reading and writing to the Typo3 cache.
Uses the Typo3 caching framework, see EXT:nnhelpers/ext_localconf.php for details
Overview of Methods
\nn\t3::Cache()->clear($identifier = NULL);
Deletes caches.
If an identifier is specified, only the caches of the specific identifier are deleted
identifier are deleted â otherwise ALL caches of all extensions and pages.
RAM caches
CachingFramework caches that were set via \nn\t3::Cache()->set()
File caches that were set via \nn\t3::Cache()->write()
// delete ALL caches â also the caches of other extensions, pages etc.
\nn\t3::Cache()->clear();
// Delete only the caches with a specific identifier
\nn\t3::Cache()->clear('nnhelpers');
Writes an entry to the Typo3 cache.
The identifier is an arbitrary string or an array that uniquely identifies the cache.
// Classic application in the controller: Get and set cacheif ($cache = \nn\t3::Cache()->get('myid')) return $cache;
...
$cache = $this->view->render();
return \nn\t3::Cache()->set('myid', $cache);
Copied!
// Use RAM cache? Set TRUE as the third parameter
\nn\t3::Cache()->set('myid', $dataToCache, true);
// Set the duration of the cache to 60 minutes
\nn\t3::Cache()->set('myid', $dataToCache, 3600);
// An array can also be specified as the key
\nn\t3::Cache()->set(['pid'=>1, 'uid'=>'7'], $html);
Copied!
@param mixed $indentifier String or array to identify the cache
@param mixed $data Data to be written to the cache. (string or array)
@param mixed $useRamCachetrue: temporary cache in $GLOBALS instead of caching framework.
Writes a PHP file that can be loaded via $cache = require('...').
Based on many core functions and extensions (e.g. mask), which place static PHP files
into the file system in order to better cache performance-intensive processes such as class paths, annotation parsing etc.
better. Deliberately do not use the core functions in order to avoid any overhead and
and to ensure the greatest possible compatibility with core updates.
Deletes caches.
If an identifier is specified, only the caches of the specific identifier are deleted
identifier are deleted â otherwise ALL caches of all extensions and pages.
RAM caches
CachingFramework caches that were set via \nn\t3::Cache()->set()
File caches that were set via \nn\t3::Cache()->write()
// delete ALL caches â also the caches of other extensions, pages etc.
\nn\t3::Cache()->clear();
// Delete only the caches with a specific identifier
\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);
Deletes the page cache. Alias to \nn\t3::Page()->clearCache()
\nn\t3::Cache()->clearPageCache( 17 ); // Delete page cache for pid=17
\nn\t3::Cache()->clearPageCache(); // clear cache of ALL pages
Copied!
@param mixed $pid pid of the page whose cache is to be cleared or leave empty for all pages
Writes an entry to the Typo3 cache.
The identifier is an arbitrary string or an array that uniquely identifies the cache.
// Classic application in the controller: Get and set cacheif ($cache = \nn\t3::Cache()->get('myid')) return $cache;
...
$cache = $this->view->render();
return \nn\t3::Cache()->set('myid', $cache);
Copied!
// Use RAM cache? Set TRUE as the third parameter
\nn\t3::Cache()->set('myid', $dataToCache, true);
// Set the duration of the cache to 60 minutes
\nn\t3::Cache()->set('myid', $dataToCache, 3600);
// An array can also be specified as the key
\nn\t3::Cache()->set(['pid'=>1, 'uid'=>'7'], $html);
Copied!
@param mixed $indentifier String or array to identify the cache
@param mixed $data Data to be written to the cache. (string or array)
@param mixed $useRamCachetrue: temporary cache in $GLOBALS instead of caching framework.
Writes a PHP file that can be loaded via $cache = require('...').
Based on many core functions and extensions (e.g. mask), which place static PHP files
into the file system in order to better cache performance-intensive processes such as class paths, annotation parsing etc.
better. Deliberately do not use the core functions in order to avoid any overhead and
and to ensure the greatest possible compatibility with core updates.
Loads the content for a specific column(colPos) and page.
If no pageUid is specified, it uses the current page.
With slide, the content element of the parent page is fetched if no content element exists in the column on the specified page.
Get content of colPos = 110 from the current page:
\nn\t3::Content()->column( 110 );
Copied!
Get content of colPos = 110 from the current page. If there is no content in the column on the current page, use the content from the parent page:
\nn\t3::Content()->column( 110, true );
Copied!
Get the content of colPos = 110 from the page with id 99:
\nn\t3::Content()->column( 110, 99 );
Copied!
Get content of colPos = 110 from the page with id 99. If there is no content in the column on page 99, use the content from the parent page of page 99:
The data records are localized automatically - except $localize is set to false
is set to false. See \nn\t3::Content()->get() for more $localize options.
Get data in a DIFFERENT language than the one set in the frontend.
Takes into account the fallback chain of the language that was set in the site config
Loads the content for a specific column(colPos) and page.
If no pageUid is specified, it uses the current page.
With slide, the content element of the parent page is fetched if no content element exists in the column on the specified page.
Get content of colPos = 110 from the current page:
\nn\t3::Content()->column( 110 );
Copied!
Get content of colPos = 110 from the current page. If there is no content in the column on the current page, use the content from the parent page:
\nn\t3::Content()->column( 110, true );
Copied!
Get the content of colPos = 110 from the page with id 99:
\nn\t3::Content()->column( 110, 99 );
Copied!
Get content of colPos = 110 from the page with id 99. If there is no content in the column on page 99, use the content from the parent page of page 99:
The data records are localized automatically - except $localize is set to false
is set to false. See \nn\t3::Content()->get() for more $localize options.
Get data in a DIFFERENT language than the one set in the frontend.
Takes into account the fallback chain of the language that was set in the site config
Converting arrays to models, models to JSONs, arrays to ObjectStorages,
hex colors to RGB and much more that somehow has to do with converting things.
has to do with converting things.
Converts a human-readable specification of bytes/megabytes into a byte integer.
Extremely tolerant when it comes to spaces, upper/lower case and commas instead of periods.
Can also automatically generate FileReferences.
In this example, a new model of the type \Nng\Model\Name is created and then
then persisted in the database. The falMedia field is an ObjectStorage
with FileReferences. The FileReferences are created automatically!
Hint
To update an existing model with data from an array, there is the method
there is the method $updatedModel = \nn\t3::Obj( $prevModel )->merge( $data );
Converts a human-readable specification of bytes/megabytes into a byte integer.
Extremely tolerant when it comes to spaces, upper/lower case and commas instead of periods.
Can also automatically generate FileReferences.
In this example, a new model of the type \Nng\Model\Name is created and then
then persisted in the database. The falMedia field is an ObjectStorage
with FileReferences. The FileReferences are created automatically!
Hint
To update an existing model with data from an array, there is the method
there is the method $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]);
}
Outputs the complete, compiled query as a readable string, as it is later executed in the database
executed later in the database e.g. SELECT FROM fe_users WHERE ...
// Output statement directly in the browser
\nn\t3::Db()->debug( $query );
// Return statement as a string, do not output automaticallyecho \nn\t3::Db()->debug( $query, true );
Delete database entry. Small and fine.
Either a table name and the UID can be transferred - or a model.
Delete a data record by table name and uid or any constraint:
// Deletion based on the uid
\nn\t3::Db()->delete('table', $uid);
// Delete based on a custom field
\nn\t3::Db()->delete('table', ['uid_local'=>$uid]);
// Delete entry completely and irrevocably (do not just remove via flag deleted = 1)
\nn\t3::Db()->delete('table', $uid, true);
Radical removal of all traces of a data set including the physical SysFiles,
linked to the model. To be used with caution, as no relations
are checked for the model to be deleted.
The data is returned as an array - this is (unfortunately) still the absolute
most performant way to fetch many data records from a table, since no DataMapper
has to parse the individual rows.
// Get all data records. "hidden" is taken into account.
\nn\t3::Db()->findAll('fe_users');
// Also fetch data records that are "hidden"
\nn\t3::Db()->findAll('fe_users', true);
Finds an entry based on the UID.
Also works if the frontend has not yet been initialized,
e.g. while AuthentificationService is running or in the scheduler.
Finds ALL entries that contain a value from the $values array in the $column column.
Also works if the frontend has not yet been initialized.
Alias to \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']);
Finds ALL entries that do NOT contain a value from the $values array in the $column column.
Also works if the frontend has not yet been initialized.
// 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']);
"Repairs" the SysFileReferences for models that have a property
that only referencea FileReferenceinstead of an ObjectStorage
instead of a FileReference. At the moment, it is unclear why TYPO3 has included these
persists them in the table sys_file_reference, but empties the field tablenames
field â or does not set uid_foreign. With an `ObjectStorage
the problem does not occur.`
// must happen directly after persisting the model
\nn\t3::Db()->fixFileReferencesForModel( $model );
Get one or more domain models/entities using a uid
A single $uid or a list of $uids can be passed.
Returns the "real" model/object including all relations,
analogous to a query via the repository.
// Get a single model by its uid
$model = \nn\t3::Db()->get( 1, \Nng\MyExt\Domain\Model\Name::class );
// Get an array of models based on their uids
$modelArray = \nn\t3::Db()->get( [1,2,3], \Nng\MyExt\Domain\Model\Model\Name::class );
// Also returns hidden models
$modelArrayWithHidden = \nn\t3::Db()->get( [1,2,3], \Nng\MyExt\Domain\Model\Name::class, true );
// Get fields based on the TCA array
\nn\t3::Db()->getColumns( 'tablename' );
// Determine fields via the SchemaManager
\nn\t3::Db()->getColumns( 'tablename', true );
Create database entry OR update an existing data record.
Decides independently whether the entry should be added to the database via UPDATE or INSERT
or whether an existing data record needs to be updated. The data is
persisted directly!
Example for transferring a table name and an array:
// no uid transferred? Then INSERT a new data set
\nn\t3::Db()->save('table', ['bodytext'=>'...']);
// pass uid? Then UPDATE existing data
\nn\t3::Db()->save('table', ['uid'=>123, 'bodytext'=>'...']);
Copied!
Example for transferring a domain model:
// new model? Is inserted via $repo->add()
$model = new \My\Nice\Model();
$model->setBodytext('...');
$persistedModel = \nn\t3::Db()->save( $model );
// existing model? Is updated via $repo->update()
$model = $myRepo->findByUid(123);
$model->setBodytext('...');
$persistedModel = \nn\t3::Db()->save( $model );
Add constraint for sys_file_reference to a QueryBuilder.
Restricts the results to whether there is a FAL relation.
$queryBuilder = \nn\t3::Db()->getQueryBuilder( $table );
// Only fetch datasets that have at least one SysFileReference for falfield
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield' );
// ... that do NOT have a SysFileReference for falfield
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', false );
// ... which have EXACTLY 2 SysFileReferences
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', 2 );
// ... that have 2 or less (less than or equal) SysFileReferences
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', 2, 'lte' );
Sorts the results of a query according to an array and a specific field.
Solves the problem that an ->in() query does not return the results
in the order of the passed IDs. Example:
| $query->matching($query->in('uid', [3,1,2])); does not necessarily come
return in the order [3,1,2].
Send a "raw" query to the database.
Closer to the database is not possible. You are responsible for everything yourself.
Injections are only opposed by your (hopefully sufficient :) intelligence.
Helps, for example, with queries of tables that are not part of the Typo3 installation and
therefore could not be reached via the normal QueryBuilder.
// ALWAYS escape variables via!
$keyword = \nn\t3::Db()->quote('search term');
$rows = \nn\t3::Db()->statement( "SELECT FROM tt_news WHERE bodytext LIKE '%{$keyword}%'");
// or better use prepared statements:
$rows = \nn\t3::Db()->statement( 'SELECT FROM tt_news WHERE bodytext LIKE :str', ['str'=>"%{$keyword}%"] );
// Types can be passed (this is determined automatically for arrays)
$rows = \nn\t3::Db()->statement( 'SELECT FROM tt_news WHERE uid IN (:uids)', ['uids'=>[1,2,3]], ['uids'=>Connection::PARAM_INT_ARRAY] );
Copied!
With a SELECT statement, the rows from the database are returned as an array.
For all other statements (e.g. UPDATE or DELETE), the number of affected rows is returned.
Update database entry. Quick and easy.
The update can be done either by table name and data array.
Or you can pass a model.
Examples:
// 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!
With true instead of a $uid ALL records of the table are updated.
// UPDATE table SET test='1' WHERE 1
\nn\t3::Db()->update('table', ['test'=>1], true);
Copied!
Instead of a table name, a simple model can also be passed.
The repository is determined automatically and the model is persisted directly.
Outputs the complete, compiled query as a readable string, as it is later executed in the database
executed later in the database e.g. SELECT FROM fe_users WHERE ...
// Output statement directly in the browser
\nn\t3::Db()->debug( $query );
// Return statement as a string, do not output automaticallyecho \nn\t3::Db()->debug( $query, true );
Delete database entry. Small and fine.
Either a table name and the UID can be transferred - or a model.
Delete a data record by table name and uid or any constraint:
// Deletion based on the uid
\nn\t3::Db()->delete('table', $uid);
// Delete based on a custom field
\nn\t3::Db()->delete('table', ['uid_local'=>$uid]);
// Delete entry completely and irrevocably (do not just remove via flag deleted = 1)
\nn\t3::Db()->delete('table', $uid, true);
Radical removal of all traces of a data set including the physical SysFiles,
linked to the model. To be used with caution, as no relations
are checked for the model to be deleted.
The data is returned as an array - this is (unfortunately) still the absolute
most performant way to fetch many data records from a table, since no DataMapper
has to parse the individual rows.
// Get all data records. "hidden" is taken into account.
\nn\t3::Db()->findAll('fe_users');
// Also fetch data records that are "hidden"
\nn\t3::Db()->findAll('fe_users', true);
Finds an entry based on the UID.
Also works if the frontend has not yet been initialized,
e.g. while AuthentificationService is running or in the scheduler.
Finds ALL entries that contain a value from the $values array in the $column column.
Also works if the frontend has not yet been initialized.
Alias to \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']);
Finds ALL entries that do NOT contain a value from the $values array in the $column column.
Also works if the frontend has not yet been initialized.
// 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']);
"Repairs" the SysFileReferences for models that have a property
that only referencea FileReferenceinstead of an ObjectStorage
instead of a FileReference. At the moment, it is unclear why TYPO3 has included these
persists them in the table sys_file_reference, but empties the field tablenames
field â or does not set uid_foreign. With an `ObjectStorage
the problem does not occur.`
// must happen directly after persisting the model
\nn\t3::Db()->fixFileReferencesForModel( $model );
Get one or more domain models/entities using a uid
A single $uid or a list of $uids can be passed.
Returns the "real" model/object including all relations,
analogous to a query via the repository.
// Get a single model by its uid
$model = \nn\t3::Db()->get( 1, \Nng\MyExt\Domain\Model\Name::class );
// Get an array of models based on their uids
$modelArray = \nn\t3::Db()->get( [1,2,3], \Nng\MyExt\Domain\Model\Model\Name::class );
// Also returns hidden models
$modelArrayWithHidden = \nn\t3::Db()->get( [1,2,3], \Nng\MyExt\Domain\Model\Name::class, true );
// Get fields based on the TCA array
\nn\t3::Db()->getColumns( 'tablename' );
// Determine fields via the SchemaManager
\nn\t3::Db()->getColumns( 'tablename', true );
Copied!
@param string $table
@param boolean $useSchemaManager
@return array
Source Code
publicfunctiongetColumns( $table = '', $useSchemaManager = false ){
$cols = isset($GLOBALS['TCA'][$table]) ? $GLOBALS['TCA'][$table]['columns'] : [];
// Diese Felder sind nicht ausdrücklich im TCA, aber für Abfrage legitimif ($cols) {
$cols = \nn\t3::Arrays( $cols )->merge(['uid'=>'uid', 'pid'=>'pid', 'tstamp'=>'tstamp', 'crdate'=>'crdate', 'endtime'=>'endtime', 'starttime'=>'starttime', 'deleted'=>'deleted', 'disable'=>'disable']);
}
// Keine cols ermittelt, weil nicht im TCA registriert – oder Abfrage erzwungenif (!$cols || $useSchemaManager) {
$cols = GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable($table)
->createSchemaManager()
->listTableColumns($table);
}
foreach ($cols as $k=>$v) {
$cols[GeneralUtility::underscoredToLowerCamelCase($k)] = $v;
}
return $cols;
}
Create database entry OR update an existing data record.
Decides independently whether the entry should be added to the database via UPDATE or INSERT
or whether an existing data record needs to be updated. The data is
persisted directly!
Example for transferring a table name and an array:
// no uid transferred? Then INSERT a new data set
\nn\t3::Db()->save('table', ['bodytext'=>'...']);
// pass uid? Then UPDATE existing data
\nn\t3::Db()->save('table', ['uid'=>123, 'bodytext'=>'...']);
Copied!
Example for transferring a domain model:
// new model? Is inserted via $repo->add()
$model = new \My\Nice\Model();
$model->setBodytext('...');
$persistedModel = \nn\t3::Db()->save( $model );
// existing model? Is updated via $repo->update()
$model = $myRepo->findByUid(123);
$model->setBodytext('...');
$persistedModel = \nn\t3::Db()->save( $model );
Add constraint for sys_file_reference to a QueryBuilder.
Restricts the results to whether there is a FAL relation.
$queryBuilder = \nn\t3::Db()->getQueryBuilder( $table );
// Only fetch datasets that have at least one SysFileReference for falfield
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield' );
// ... that do NOT have a SysFileReference for falfield
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', false );
// ... which have EXACTLY 2 SysFileReferences
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', 2 );
// ... that have 2 or less (less than or equal) SysFileReferences
\nn\t3::Db()->setFalConstraint( $queryBuilder, 'tx_myext_tablename', 'falfield', 2, 'lte' );
Sorts the results of a query according to an array and a specific field.
Solves the problem that an ->in() query does not return the results
in the order of the passed IDs. Example:
| $query->matching($query->in('uid', [3,1,2])); does not necessarily come
return in the order [3,1,2].
Send a "raw" query to the database.
Closer to the database is not possible. You are responsible for everything yourself.
Injections are only opposed by your (hopefully sufficient :) intelligence.
Helps, for example, with queries of tables that are not part of the Typo3 installation and
therefore could not be reached via the normal QueryBuilder.
// ALWAYS escape variables via!
$keyword = \nn\t3::Db()->quote('search term');
$rows = \nn\t3::Db()->statement( "SELECT FROM tt_news WHERE bodytext LIKE '%{$keyword}%'");
// or better use prepared statements:
$rows = \nn\t3::Db()->statement( 'SELECT FROM tt_news WHERE bodytext LIKE :str', ['str'=>"%{$keyword}%"] );
// Types can be passed (this is determined automatically for arrays)
$rows = \nn\t3::Db()->statement( 'SELECT FROM tt_news WHERE uid IN (:uids)', ['uids'=>[1,2,3]], ['uids'=>Connection::PARAM_INT_ARRAY] );
Copied!
With a SELECT statement, the rows from the database are returned as an array.
For all other statements (e.g. UPDATE or DELETE), the number of affected rows is returned.
@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;
}
Update database entry. Quick and easy.
The update can be done either by table name and data array.
Or you can pass a model.
Examples:
// 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!
With true instead of a $uid ALL records of the table are updated.
// UPDATE table SET test='1' WHERE 1
\nn\t3::Db()->update('table', ['test'=>1], true);
Copied!
Instead of a table name, a simple model can also be passed.
The repository is determined automatically and the model is persisted directly.
Checks whether the hash of a password and a password match.
Application: Password hash of a fe_user in the database with the submitted password
compare.
In contrast to \nn\t3::Encrypt()->hash(), an encrypted value can be decrypted again using \nn\t3::Encrypt()->decode()
can be decrypted again. This method is therefore not suitable for storing sensitive data such as passwords
in a database. Nevertheless, the level of protection is relatively high, as even identical data encrypted with
encrypted with the same salting key look different.
A salting key is generated for the encryption and stored in the nnhelpers Extension Manager.
This key is unique for each installation. If it is changed, data that has already been encrypted cannot be
be decrypted again.
Retrieves the Enryption / Salting Key from the extension configuration for nnhelpers
If no key has been set in the Extension Manager, it is generated automatically
and saved in the LocalConfiguration.php.
Checks whether the hash needs to be updated because it does not correspond to the current encryption algorithm.
When updating Typo3 to a new LTS, the hashing algorithm of the passwords in the database is also often
is improved. This method checks whether the transferred hash is still up-to-date or needs to be updated.
Get session hash for fe_sessions.ses_id
Corresponds to the value that is stored for the fe_typo_user cookie in the database.
In TYPO3 < v10 an unchanged value is returned here. As of TYPO3 v10, the session ID is stored in the
cookie fe_typo_user is no longer stored directly in the database, but hashed.
See: TYPO3\CMS\Core\Session\Backend\DatabaseSessionBackend->hash().
Create a JWT (Json Web Token), sign it and return it base64-encoded.
Do not forget: A JWT is "forgery-proof" because the signature hash can only be generated with
can only be generated with the correct key/salt - but all data in the JWT can be read by anyone
can be viewed through base64_decode(). A JWT is by no means suitable for storing sensitive data such as
passwords or logins!
Parse a JWT (Json Web Token) and check the signature.
If the signature is valid (and therefore the payload has not been manipulated), the
payload is returned. If the signature is invalid, FALSE is returned.
Checks whether the hash of a password and a password match.
Application: Password hash of a fe_user in the database with the submitted password
compare.
In contrast to \nn\t3::Encrypt()->hash(), an encrypted value can be decrypted again using \nn\t3::Encrypt()->decode()
can be decrypted again. This method is therefore not suitable for storing sensitive data such as passwords
in a database. Nevertheless, the level of protection is relatively high, as even identical data encrypted with
encrypted with the same salting key look different.
A salting key is generated for the encryption and stored in the nnhelpers Extension Manager.
This key is unique for each installation. If it is changed, data that has already been encrypted cannot be
be decrypted again.
Retrieves the Enryption / Salting Key from the extension configuration for nnhelpers
If no key has been set in the Extension Manager, it is generated automatically
and saved in the LocalConfiguration.php.
\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 = '');
Simple hashing, e.g. when checking a uid against a hash.
Checks whether the hash needs to be updated because it does not correspond to the current encryption algorithm.
When updating Typo3 to a new LTS, the hashing algorithm of the passwords in the database is also often
is improved. This method checks whether the transferred hash is still up-to-date or needs to be updated.
Get session hash for fe_sessions.ses_id
Corresponds to the value that is stored for the fe_typo_user cookie in the database.
In TYPO3 < v10 an unchanged value is returned here. As of TYPO3 v10, the session ID is stored in the
cookie fe_typo_user is no longer stored directly in the database, but hashed.
See: TYPO3\CMS\Core\Session\Backend\DatabaseSessionBackend->hash().
Create a JWT (Json Web Token), sign it and return it base64-encoded.
Do not forget: A JWT is "forgery-proof" because the signature hash can only be generated with
can only be generated with the correct key/salt - but all data in the JWT can be read by anyone
can be viewed through base64_decode(). A JWT is by no means suitable for storing sensitive data such as
passwords or logins!
Parse a JWT (Json Web Token) and check the signature.
If the signature is valid (and therefore the payload has not been manipulated), the
payload is returned. If the signature is invalid, FALSE is returned.
Everything you need to know about the application's environment.
From the user's language ID and the baseUrl to the question of which extensions are running.
Returns the default language (Default Language). In TYPO3, this is always the language with ID 0
The languages must be defined in the YAML site configuration.
Returns a list of the languages that should be used if, for example
e.g. a page or element does not exist in the desired language.
Important: The fallback chain contains in the first place the current or in $langUid
transferred language.
// Use settings for current language (see Site-Config YAML)
\nn\t3::Environment()->getLanguageFallbackChain(); // --> e.g. [0] or [1,0]// Get settings for a specific language
\nn\t3::Environment()->getLanguageFallbackChain( 1 );
// --> [1,0] - if fallback was defined in Site-Config and the fallbackMode is set to "fallback"// --> [1] - if there is no fallback or the fallbackMode is set to "strict"
Return maximum upload size for files from the frontend.
This specification is the value that was defined in php.ini and, if necessary
was overwritten via the .htaccess.
\nn\t3::Environment()->getPostMaxSize(); // e.g. '1048576' for 1MB
This is an array with all folders that must be parsed by class when autoloading / bootstrapping TYPO3
have to be parsed. In a TYPO3 extension, this is the Classes folder by default.
The list is generated by Composer/TYPO3.
An array is returned. Key is Vendor\Namespace\, Value is an array with paths to the folders,
which are searched recursively for classes. It does not matter whether TYPO3 is running in composer
mode or not.
Get the current site object.
This object can be used to access the configuration from the site YAML file from TYPO3 9 onwards, for example.
In the context of a MiddleWare, the site may not yet be parsed / loaded.
In this case, the $request from the MiddleWare can be passed to determine the site.
See also \nn\t3::Settings()->getSiteConfig() to read the site configuration.
Get the absolute path to the /var directory of Typo3.
This directory stores temporary cache files.
Depending on the version of Typo3 and installation type (Composer or Non-Composer mode)
this directory can be found in different locations.
Returns the default language (Default Language). In TYPO3, this is always the language with ID 0
The languages must be defined in the YAML site configuration.
Returns a list of the languages that should be used if, for example
e.g. a page or element does not exist in the desired language.
Important: The fallback chain contains in the first place the current or in $langUid
transferred language.
// Use settings for current language (see Site-Config YAML)
\nn\t3::Environment()->getLanguageFallbackChain(); // --> e.g. [0] or [1,0]// Get settings for a specific language
\nn\t3::Environment()->getLanguageFallbackChain( 1 );
// --> [1,0] - if fallback was defined in Site-Config and the fallbackMode is set to "fallback"// --> [1] - if there is no fallback or the fallbackMode is set to "strict"
Return maximum upload size for files from the frontend.
This specification is the value that was defined in php.ini and, if necessary
was overwritten via the .htaccess.
\nn\t3::Environment()->getPostMaxSize(); // e.g. '1048576' for 1MB
This is an array with all folders that must be parsed by class when autoloading / bootstrapping TYPO3
have to be parsed. In a TYPO3 extension, this is the Classes folder by default.
The list is generated by Composer/TYPO3.
An array is returned. Key is Vendor\Namespace\, Value is an array with paths to the folders,
which are searched recursively for classes. It does not matter whether TYPO3 is running in composer
mode or not.
Get the current site object.
This object can be used to access the configuration from the site YAML file from TYPO3 9 onwards, for example.
In the context of a MiddleWare, the site may not yet be parsed / loaded.
In this case, the $request from the MiddleWare can be passed to determine the site.
See also \nn\t3::Settings()->getSiteConfig() to read the site configuration.
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();
Get the absolute path to the /var directory of Typo3.
This directory stores temporary cache files.
Depending on the version of Typo3 and installation type (Composer or Non-Composer mode)
this directory can be found in different locations.
Convert a file to a FileReference object and
to the Property or ObjectStorage of a model.
See also: \nn\t3::Fal()->setInModel( $member, 'falslideshow', $imagesToSet ); with the
array of multiple images can be attached to an ObjectStorage.
Deletes the cache for the image sizes of a FAL including the converted images
If, for example, the f:image-ViewHelper is used, all calculated image sizes are
are saved in the sys_file_processedfile table. If the original image changes,
an image from the cache may still be accessed.
@param string $storageConfig Path/folder in which the FAL file is to be saved (e.g. 'fileadmin/projectdata/')
@param string $srcFile Source file to be converted to FAL (e.g. 'uploads/tx_nnfesubmit/example.jpg')
Can also be URL to YouTube/Vimeo video (e.g. https://www. youtube.com/watch?v=7Bb5jXhwnRY)
@param boolean $keepSrcFile Copy source file only, do not move?
@param boolean $forceCreateNew Should a new file always be created? If not, it may return an existing file object
Convert a file to a FileReference object and prepare it for attach() to an existing
model and field / property. The FileReference is not automatically
attached to the model. To set the FAL directly in the model, the helper
| \nn\t3::Fal()->attach( $model, $field, $itemData ) can be used.
Creates new entry in sys_file
Searches all sys_file_storage entries to see whether the path to the $file already exists as storage.
If not, a new storage is created.
Deletes the physical files for a model (or a single field of the
field of the model) from the server.
// Delete ALL files of the entire model
\nn\t3::Fal()->deleteForModel( $model );
// Delete ALL files from the "images" field
\nn\t3::Fal()->deleteForModel( $model, 'images' );
Deletes all physical thumbnail files that were generated for an image incl.
the data records in the sys_file_processedfile table.
The original image, which was passed as the $path argument, is not deleted.
The whole process forces the thumbnails for an image to be regenerated if, for example, the
source image has changed but the file name has remained the same.
Another use case: Cleaning up files on the server, e.g. because sensitive, personal data is to be
data including all generated thumbnails should be deleted.
Deletes a SysFile (data record from table sys_file) and all associated SysFileReferences.
A radical way to completely remove an image from the Typo3 indexing.
The physical file is not deleted from the server!
See \nn\t3::File()->unlink() to delete the physical file.
See \nn\t3::Fal()->detach( $model, $field ); to delete from a model.
Empties an ObjectStorage in a model or removes a single
individual object from the model or an ObjectStorage.
In the example, image can be an ObjectStorage or a single FileReference:
Retrieves / converts to a TYPO3CMSCoreResourceFileReference object (sys_file_reference)
"Smart" variant of \TYPO3\CMS\Extbase\Service\ImageService->getImage()
Calculates an image via maxWidth, maxHeight, cropVariant etc.
Returns URI to the image as a string. Helpful for calculating thumbnails in the backend.
Alias to \nn\t3::File()->process()
Replaces a FileReference or ObjectStorage in a model with images.
Typical use case: A FAL image is to be changed via an upload form in the frontend.
be able to be changed.
For each image, the system checks whether a FileReference already exists in the model.
Existing FileReferences are not overwritten, otherwise any
captions or cropping instructions would be lost!
Attention! The model is automatically persisted!
$newModel = new \My\Extension\Domain\Model\Example();
\nn\t3::Fal()->setInModel( $newModel, 'falslideshow', 'path/to/file.jpg' );
echo $newModel->getUid(); // Model has been persisted!
Convert a FileReference into an array.
Contains publicUrl, title, alternative, crop etc. of the FileReference.
Alias to \nn\t3::Obj()->toArray( $fileReference );
\nn\t3::Fal()->toArray( $fileReference ); // results in ['publicUrl'=>'fileadmin/...', 'title'=>'...']
Convert a file to a FileReference object and
to the Property or ObjectStorage of a model.
See also: \nn\t3::Fal()->setInModel( $member, 'falslideshow', $imagesToSet ); with the
array of multiple images can be attached to an ObjectStorage.
Deletes the cache for the image sizes of a FAL including the converted images
If, for example, the f:image-ViewHelper is used, all calculated image sizes are
are saved in the sys_file_processedfile table. If the original image changes,
an image from the cache may still be accessed.
@param string $storageConfig Path/folder in which the FAL file is to be saved (e.g. 'fileadmin/projectdata/')
@param string $srcFile Source file to be converted to FAL (e.g. 'uploads/tx_nnfesubmit/example.jpg')
Can also be URL to YouTube/Vimeo video (e.g. https://www. youtube.com/watch?v=7Bb5jXhwnRY)
@param boolean $keepSrcFile Copy source file only, do not move?
@param boolean $forceCreateNew Should a new file always be created? If not, it may return an existing file object
Convert a file to a FileReference object and prepare it for attach() to an existing
model and field / property. The FileReference is not automatically
attached to the model. To set the FAL directly in the model, the helper
| \nn\t3::Fal()->attach( $model, $field, $itemData ) can be used.
Creates new entry in sys_file
Searches all sys_file_storage entries to see whether the path to the $file already exists as storage.
If not, a new storage is created.
Deletes the physical files for a model (or a single field of the
field of the model) from the server.
// Delete ALL files of the entire model
\nn\t3::Fal()->deleteForModel( $model );
// Delete ALL files from the "images" field
\nn\t3::Fal()->deleteForModel( $model, 'images' );
Deletes all physical thumbnail files that were generated for an image incl.
the data records in the sys_file_processedfile table.
The original image, which was passed as the $path argument, is not deleted.
The whole process forces the thumbnails for an image to be regenerated if, for example, the
source image has changed but the file name has remained the same.
Another use case: Cleaning up files on the server, e.g. because sensitive, personal data is to be
data including all generated thumbnails should be deleted.
Deletes a SysFile (data record from table sys_file) and all associated SysFileReferences.
A radical way to completely remove an image from the Typo3 indexing.
The physical file is not deleted from the server!
See \nn\t3::File()->unlink() to delete the physical file.
See \nn\t3::Fal()->detach( $model, $field ); to delete from a model.
Empties an ObjectStorage in a model or removes a single
individual object from the model or an ObjectStorage.
In the example, image can be an ObjectStorage or a single FileReference:
Retrieves / converts to a TYPO3CMSCoreResourceFileReference object (sys_file_reference)
"Smart" variant of \TYPO3\CMS\Extbase\Service\ImageService->getImage()
Calculates an image via maxWidth, maxHeight, cropVariant etc.
Returns URI to the image as a string. Helpful for calculating thumbnails in the backend.
Alias to \nn\t3::File()->process()
Replaces a FileReference or ObjectStorage in a model with images.
Typical use case: A FAL image is to be changed via an upload form in the frontend.
be able to be changed.
For each image, the system checks whether a FileReference already exists in the model.
Existing FileReferences are not overwritten, otherwise any
captions or cropping instructions would be lost!
Attention! The model is automatically persisted!
$newModel = new \My\Extension\Domain\Model\Example();
\nn\t3::Fal()->setInModel( $newModel, 'falslideshow', 'path/to/file.jpg' );
echo $newModel->getUid(); // Model has been persisted!
@param mixed $model The model that is to be changed
@param string $fieldName Property (field name) of the ObjectStorage or FileReference
@param mixed $imagesToAdd String / array with images
| @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);
Convert a FileReference into an array.
Contains publicUrl, title, alternative, crop etc. of the FileReference.
Alias to \nn\t3::Obj()->toArray( $fileReference );
\nn\t3::Fal()->toArray( $fileReference ); // results in ['publicUrl'=>'fileadmin/...', 'title'=>'...']
Download as ZIP requires the PHP extension gmp. If extension is not available,
the .tar variant is used. On Mac, the function is always used due to
security warnings from the Finder, the function always uses tar
If an array is passed, a tar/zip download is started.
By passing an associative array with file name as key and path in the archive as value
the file and folder structure in the zip archive can be determined.
Finds a matching sys_file_storage for a file or folder path.
To do this, searches through all sys_file_storage entries and compares
whether the basePath of the storage matches the path of the file.
Can be the absolute path to the tmp file of the upload â or a TYPO3\CMS\Core\Http\UploadedFile,
which can be retrieved in the controller via $this->request->getUploadedFiles().
Calculates an image via maxWidth, maxHeight etc.
Simple version of \nn\t3::File()->processImage()
Can be used if only the generation of reduced images is required
without taking into account corrections to camera alignment etc.
Since the crop settings are stored in FileReference and not File,
cropVariant only works when a FileReference is passed.
Can be called directly after upload_copy_move().
Corrects the orientation of the image, which may have been saved in EXIF data.
Simply use the method \nn\t3::File()->process() for the maxWidth statement.
Instructions for $processing:
| correctOrientation => Correct rotation (e.g. because photo was uploaded from smartphone)
Creates a unique file name for the file if there is already
a file with an identical name already exists in the target directory
already exists in the target directory.
Deletes a file completely from the server.
Also deletes all sys_file and sys_file_references that refer to the file.
For security reasons, no PHP or HTML files can be deleted.
\nn\t3::File()->unlink('fileadmin/image.jpg'); // Path to the image
\nn\t3::File()->unlink('/abs/path/to/file/fileadmin/image.jpg'); // absolute path to the image
\nn\t3::File()->unlink('1:/my/image.jpg'); // Combined identifier notation
\nn\t3::File()->unlink( $model->getImage() ); // \TYPO3\CMS\Extbase\Domain\Model\FileReference
\nn\t3::File()->unlink( $falFile ); // \TYPO3\CMS\Core\Resource\FileReference
Download as ZIP requires the PHP extension gmp. If extension is not available,
the .tar variant is used. On Mac, the function is always used due to
security warnings from the Finder, the function always uses tar
If an array is passed, a tar/zip download is started.
By passing an associative array with file name as key and path in the archive as value
the file and folder structure in the zip archive can be determined.
Finds a matching sys_file_storage for a file or folder path.
To do this, searches through all sys_file_storage entries and compares
whether the basePath of the storage matches the path of the file.
Can be the absolute path to the tmp file of the upload â or a TYPO3\CMS\Core\Http\UploadedFile,
which can be retrieved in the controller via $this->request->getUploadedFiles().
Calculates an image via maxWidth, maxHeight etc.
Simple version of \nn\t3::File()->processImage()
Can be used if only the generation of reduced images is required
without taking into account corrections to camera alignment etc.
Since the crop settings are stored in FileReference and not File,
cropVariant only works when a FileReference is passed.
Can be called directly after upload_copy_move().
Corrects the orientation of the image, which may have been saved in EXIF data.
Simply use the method \nn\t3::File()->process() for the maxWidth statement.
Instructions for $processing:
| correctOrientation => Correct rotation (e.g. because photo was uploaded from smartphone)
| @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);
Retrieves the content of a file
\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 = '');
relative path (from the current script) to a file / directory.
If no path is specified, the Typo3 root directory is returned.
publicfunctiontype($filename = null){
if (!$filename) returnfalse;
$suffix = $this->suffix($filename);
foreach (self::$TYPES as $k => $arr) {
if (in_array($suffix, $arr)) return $k;
}
return'other';
}
Copied!
File::uniqueFilename()
\nn\t3::File()->uniqueFilename($filename = '');
Creates a unique file name for the file if there is already
a file with an identical name already exists in the target directory
already exists in the target directory.
Deletes a file completely from the server.
Also deletes all sys_file and sys_file_references that refer to the file.
For security reasons, no PHP or HTML files can be deleted.
\nn\t3::File()->unlink('fileadmin/image.jpg'); // Path to the image
\nn\t3::File()->unlink('/abs/path/to/file/fileadmin/image.jpg'); // absolute path to the image
\nn\t3::File()->unlink('1:/my/image.jpg'); // Combined identifier notation
\nn\t3::File()->unlink( $model->getImage() ); // \TYPO3\CMS\Extbase\Domain\Model\FileReference
\nn\t3::File()->unlink( $falFile ); // \TYPO3\CMS\Core\Resource\FileReference
Copied!
| @return boolean
Source Code
publicfunctionunlink($file = null){
$file = $this->getPublicUrl($file);
if (!trim($file)) returnfalse;
$file = $this->absPath($this->absPath($file));
\nn\t3::Fal()->deleteSysFile($file);
if (!$this->exists($file)) returnfalse;
if (!$this->isAllowed($file)) returnfalse;
@unlink($file);
if (file_exists($file)) returnfalse;
returntrue;
}
Inserts options from TypoScript for selection in a FlexForm or TCA.
select
nn\t3\Flexform->insertOptions
plugin.tx_extname.settings.templates
tx_extname.colors
value
1
Nothing
1
Copied!
Various types of structure are permitted for the Typoscript:
plugin.tx_extname.settings.templates {
# Direct key => label pairs
small = Small Design
# ... or: Label set in subarray
mid {
label = Mid Design
}
# ... or: Key set in subarray, practical e.g. for CSS classes10 {
label = Big Design
classes = big big-thing
}
# ... or a userFunc. Returns one of the variants above as an array30 {
userFunc = nn\t3\Flexform->getOptions
}
}
Copied!
The selection can be restricted to certain controller actions in the TypoScript.
In this example, the "Yellow" option is only displayed if the switchableControllerAction
| Category->list has been selected.
Inserts options from TypoScript for selection in a FlexForm or TCA.
select
nn\t3\Flexform->insertOptions
plugin.tx_extname.settings.templates
tx_extname.colors
value
1
Nothing
1
Copied!
Various types of structure are permitted for the Typoscript:
plugin.tx_extname.settings.templates {
# Direct key => label pairs
small = Small Design
# ... or: Label set in subarray
mid {
label = Mid Design
}
# ... or: Key set in subarray, practical e.g. for CSS classes10 {
label = Big Design
classes = big big-thing
}
# ... or a userFunc. Returns one of the variants above as an array30 {
userFunc = nn\t3\Flexform->getOptions
}
}
Copied!
The selection can be restricted to certain controller actions in the TypoScript.
In this example, the "Yellow" option is only displayed if the switchableControllerAction
| Category->list has been selected.
Get user groups of the current FE user as an array.
The uids of the user groups are used as keys in the returned array.
// Minimal version: By default, Typo3 only returns title, uid and pid
\nn\t3::FrontendUser()->getCurrentUserGroups(); // [1 => ['title'=>'Group A', 'uid' => 1, 'pid'=>5]]// If true, the complete data record for the fe_user_group can be read from the DB
\nn\t3::FrontendUser()->getCurrentUserGroups( true ); // [1 => [... all fields of the DB] ]
Get user groups of the current FE user.
Alias to \nn\t3::FrontendUser()->getCurrentUserGroups();
// only load title, uid and pid of the groups
\nn\t3::FrontendUser()->getGroups();
// load complete data set of the groups
\nn\t3::FrontendUser()->getGroups( true );
Checks whether the user is currently logged in as a FE user.
Earlier: isset($GLOBALS['TSFE']) && $GLOBALS['TSFE']->loginUser
// Check after complete initialization of the front/backend
\nn\t3::FrontendUser()->isLoggedIn();
// Check using the JWT, e.g. in an eID script before authentication
\nn\t3::FrontendUser()->isLoggedIn( $request );
If no sessionID is passed, Typo3 searches for the FE user's session ID itself.
When calling this method from a MiddleWare, the request should be passed with .
This allows, for example, the global $_COOKIE value and the cookieParams.fe_typo_user in the request
before authentication via typo3/cms-frontend/authentication in a separate MiddleWare
must be set. Helpful if cross-domain authentication is required (e.g.
via Json Web Token / JWT).
// Merge session data for `shop` with new data (existing keys in `shop` are not deleted)
\nn\t3::FrontendUser()->setSessionData('store', ['a'=>1]);
// Overwrite session data for `shop` (`a` from the example above is deleted)
\nn\t3::FrontendUser()->setSessionData('store', ['b'=>1], false);
Get user groups of the current FE user as an array.
The uids of the user groups are used as keys in the returned array.
// Minimal version: By default, Typo3 only returns title, uid and pid
\nn\t3::FrontendUser()->getCurrentUserGroups(); // [1 => ['title'=>'Group A', 'uid' => 1, 'pid'=>5]]// If true, the complete data record for the fe_user_group can be read from the DB
\nn\t3::FrontendUser()->getCurrentUserGroups( true ); // [1 => [... all fields of the 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 [];
}
Get user groups of the current FE user.
Alias to \nn\t3::FrontendUser()->getCurrentUserGroups();
// only load title, uid and pid of the groups
\nn\t3::FrontendUser()->getGroups();
// load complete data set of the groups
\nn\t3::FrontendUser()->getGroups( true );
Checks whether the user is currently logged in as a FE user.
Earlier: isset($GLOBALS['TSFE']) && $GLOBALS['TSFE']->loginUser
// Check after complete initialization of the front/backend
\nn\t3::FrontendUser()->isLoggedIn();
// Check using the JWT, e.g. in an eID script before authentication
\nn\t3::FrontendUser()->isLoggedIn( $request );
If no sessionID is passed, Typo3 searches for the FE user's session ID itself.
When calling this method from a MiddleWare, the request should be passed with .
This allows, for example, the global $_COOKIE value and the cookieParams.fe_typo_user in the request
before authentication via typo3/cms-frontend/authentication in a separate MiddleWare
must be set. Helpful if cross-domain authentication is required (e.g.
via Json Web Token / JWT).
// Merge session data for `shop` with new data (existing keys in `shop` are not deleted)
\nn\t3::FrontendUser()->setSessionData('store', ['a'=>1]);
// Overwrite session data for `shop` (`a` from the example above is deleted)
\nn\t3::FrontendUser()->setSessionData('store', ['b'=>1], false);
The session ID corresponds to the TYPO3 cookie fe_typo_user. As a rule, there is one entry for
one entry in the fe_sessions table for each FE user session. Up to Typo3 v10, the
the ses_id column corresponded exactly to the cookie value.
As of Typo3 v10, the value is also hashed.
See also \nn\t3::Encrypt()->hashSessionId( $sessionId );
Create a new frontend user session in the fe_sessions table.
Either the fe_users.uid or the fe_users.username can be transferred.
The user is not automatically logged in. Instead, only a valid session
is created and prepared in the database, which Typo3 can later use for authentication.
Returns the session ID.
The session ID corresponds exactly to the value in the fe_typo_user cookie- but not necessarily the
value that is stored in fe_sessions.ses_id. The value in the database is hashed from TYPO3 v11
hashed.
If the session is to be re-established with an existing SessionId, a (non-hashed) second parameter can be used as an optional,
second parameter, a (non-hashed) SessionId can be passed:
The session ID corresponds to the TYPO3 cookie fe_typo_user. As a rule, there is one entry for
one entry in the fe_sessions table for each FE user session. Up to Typo3 v10, the
the ses_id column corresponded exactly to the cookie value.
As of Typo3 v10, the value is also hashed.
See also \nn\t3::Encrypt()->hashSessionId( $sessionId );
Create a new frontend user session in the fe_sessions table.
Either the fe_users.uid or the fe_users.username can be transferred.
The user is not automatically logged in. Instead, only a valid session
is created and prepared in the database, which Typo3 can later use for authentication.
Returns the session ID.
The session ID corresponds exactly to the value in the fe_typo_user cookie- but not necessarily the
value that is stored in fe_sessions.ses_id. The value in the database is hashed from TYPO3 v11
hashed.
If the session is to be re-established with an existing SessionId, a (non-hashed) second parameter can be used as an optional,
second parameter, a (non-hashed) SessionId can be passed:
To convert geo-coordinates into address data and vice versa, a Google Maps ApiKey
must be created and stored in the Extension Manager for nnhelpers. Alternatively, you can
a separate ApiKey can be specified during initialization:
nn\t3::Geo( $myApiKey )->getCoordinates('...');
Copied!
Overview of Methods
\nn\t3::Geo()->autoComplete($params = []);
Autocomplete search: Finds addresses (names) based on a search term
Convert geo-coordinates into address data (reverse geo coding)
If the extension nnaddress is installed, it will be used for the resolution.
// Return the first result
\nn\t3::Geo()->getAddress( 8.250693320181336, 50.08060702093021 );
// Return ALL results
\nn\t3::Geo()->getAddress( 8.250693320181336, 50.08060702093021, true );
// return ALL results in English
\nn\t3::Geo()->getAddress( 8.250693320181336, 50.08060702093021, true, 'en' );
// $lng and $lat can also be passed as an array
\nn\t3::Geo()->getAddress( ['lat'=>50.08060702093021, 'lng'=>8.250693320181336] );
// Use your own API key?
\nn\t3::Geo( $apiKey )->getAddress( 8.250693320181336, 50.08060702093021 );
Convert geo-coordinates into address data (reverse geo coding)
If the extension nnaddress is installed, it will be used for the resolution.
// Return the first result
\nn\t3::Geo()->getAddress( 8.250693320181336, 50.08060702093021 );
// Return ALL results
\nn\t3::Geo()->getAddress( 8.250693320181336, 50.08060702093021, true );
// return ALL results in English
\nn\t3::Geo()->getAddress( 8.250693320181336, 50.08060702093021, true, 'en' );
// $lng and $lat can also be passed as an array
\nn\t3::Geo()->getAddress( ['lat'=>50.08060702093021, 'lng'=>8.250693320181336] );
// Use your own API key?
\nn\t3::Geo( $apiKey )->getAddress( 8.250693320181336, 50.08060702093021 );
Uses the translations that are specified in the xlf of an extension.
These files are located by default under EXT:extname/Resources/Private/Language/locallang.xlf
or EXT:extname/Resources/Private/Language/en.locallang.xlf for the respective translation.
// Simple example:
\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');
// Replace arguments in the string: 'After the %s comes the %s' or `Before the %2$s comes the %1$s'
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress', ['one', 'two']);
// explode() the result at a separator character
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress', null, ',');
// Translate to a language other than the current frontend language
\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');
Translates a text via DeepL.
An API key must be entered in the Extension Manager.
DeepL allows the translation of up to 500,000 characters / month free of charge.
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad' );
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad', 'EN' );
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad', 1 );
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad', 'EN', 'DE' );
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad', 1, 0 );
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad', 'EN', 'DE', $apiKey );
Copied!
@param string $srcText Text to be translated
@param string|int $targetLanguageKey Target language (e.g. 'EN' or '1')
@param string|int $sourceLanguageKey Source language (e.g. 'DE' or '0')
@param string $apiKey DeepL Api key (if not defined in the ExtensionManager)
Uses the translations that are specified in the xlf of an extension.
These files are located by default under EXT:extname/Resources/Private/Language/locallang.xlf
or EXT:extname/Resources/Private/Language/en.locallang.xlf for the respective translation.
// Simple example:
\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');
// Replace arguments in the string: 'After the %s comes the %s' or `Before the %2$s comes the %1$s'
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress', ['one', 'two']);
// explode() the result at a separator character
\nn\t3::LL()->get('tx_nnaddress_domain_model_entry', 'nnaddress', null, ',');
// Translate to a language other than the current frontend language
\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');
Translates a text via DeepL.
An API key must be entered in the Extension Manager.
DeepL allows the translation of up to 500,000 characters / month free of charge.
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad' );
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad', 'EN' );
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad', 1 );
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad', 'EN', 'DE' );
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad', 1, 0 );
\nn\t3::LL()->translate( 'The horse does not eat cucumber salad', 'EN', 'DE', $apiKey );
Copied!
@param string $srcText Text to be translated
@param string|int $targetLanguageKey Target language (e.g. 'EN' or '1')
@param string|int $sourceLanguageKey Source language (e.g. 'DE' or '0')
@param string $apiKey DeepL Api key (if not defined in the ExtensionManager)
@return string
Source Code
publicfunctiontranslate( $srcText = '', $targetLanguageKey = 'EN', $sourceLanguageKey = 'DE', $apiKey = null ){
$deeplConfig = \nn\t3::Environment()->getExtConf('nnhelpers');
if (!$apiKey) {
$apiKey = $deeplConfig['deeplApiKey'] ?? false;
}
if (!$this->sysLanguages) {
$this->sysLanguages = \nn\t3::Environment()->getLanguages('languageId', 'iso-639-1');
}
// convert numeric language_uid to language stringif (is_numeric($targetLanguageKey)) {
$targetLanguageKey = $this->sysLanguages[$targetLanguageKey];
}
if (is_numeric($sourceLanguageKey)) {
$sourceLanguageKey = $this->sysLanguages[$sourceLanguageKey];
}
if (!$apiKey || !$deeplConfig['deeplApiUrl']) {
return'Bitte API Key und URL für DeepL im Extension-Manager angeben';
}
$srcText = \nn\t3::Convert($srcText)->toUTF8();
$params = [
'text' => '<LL>' . $srcText . '</LL>',
'source_lang' => strtoupper($sourceLanguageKey),
'target_lang' => strtoupper($targetLanguageKey),
'tag_handling' => 'xml',
];
$headers = [
'Authorization' => 'DeepL-Auth-Key ' . $apiKey,
];
$result = \nn\t3::Request()->POST( $deeplConfig['deeplApiUrl'], $params, $headers );
if ($result['status'] != 200) {
die('[ERROR] Fehler bei POST-Query an ' . $deeplConfig['deeplApiUrl'] . ' [' . $result['status'] . '] ' . $result['content']);
return"[ERROR] Fehler bei POST-Query an {$deeplConfig['deeplApiUrl']} [{$result['status']}, {$result['error']}]";
}
$json = json_decode( $result['content'], true ) ?: ['error' => 'JSON leer'];
if (!$json || !isset($json['translations'][0]['text'])) {
return"[ERROR] Fehler bei Übersetzung. Kein Text von DeepL zurückgegeben oder JSON konnte nicht geparsed werden.";
}
$text = $json['translations'][0]['text'] ?? '';
$text = trim(str_replace(['<LL>', '</LL>'], '', $text));
$text = str_replace( ">.\n", ">\n", $text);
return $text;
}
Get all keys of an object that have a SETTER.
In contrast to \nn\t3::Obj()->getKeys(), only the property keys are
are returned that can also be set, e.g. via setNameDerProp()
This can even be used to write / overwrite FileReferences.
In this example, $data is merged with an existing model.
| falMedia is an ObjectStorage in the example. The first element in falMedia already exists
already exists in the database(uid = 12). Only the title is updated here.
The second element in the array (without uid) is new. For this, a new
| sys_file_reference is automatically created in the database.
Hint
To create a new model with data from an array, there is the method
there is the method $newModel = \nn\t3::Convert($data)->toModel( \My\Model\Name::class );
Git an array with information back:
| type is only set if it is an array or an ObjectStorage.
| elementType is always the type of the model or the TypeHinting of the variable
Get all keys of an object that have a SETTER.
In contrast to \nn\t3::Obj()->getKeys(), only the property keys are
are returned that can also be set, e.g. via setNameDerProp()
This can even be used to write / overwrite FileReferences.
In this example, $data is merged with an existing model.
| falMedia is an ObjectStorage in the example. The first element in falMedia already exists
already exists in the database(uid = 12). Only the title is updated here.
The second element in the array (without uid) is new. For this, a new
| sys_file_reference is automatically created in the database.
Hint
To create a new model with data from an array, there is the method
there is the method $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 = '');
Parse a string with information about ObjectStorage.
Git an array with information back:
| type is only set if it is an array or an ObjectStorage.
| elementType is always the type of the model or the TypeHinting of the variable
// data of the current page
\nn\t3::Page()->getData();
// get data of the page with pid = 123
\nn\t3::Page()->getData( 123 );
// get data of the pages with pids = 123 and 456. Key of the array = pid
\nn\t3::Page()->getData( [123, 456] );
Works in any context - both from a backend module or scheduler/CLI job, as well as in the frontend context, e.g. in the controller or a ViewHelper.
Absolute URLs are generated from the backend context into the frontend. The URLs are encoded as readable URLs - the slug path or RealURL are taken into account.
Get PID of the current page.
In the frontend: The current TSFE->id
In the backend: The page that was selected in the page tree
Without context: The pid of the site root
// data of the current page
\nn\t3::Page()->getData();
// get data of the page with pid = 123
\nn\t3::Page()->getData( 123 );
// get data of the pages with pids = 123 and 456. Key of the array = pid
\nn\t3::Page()->getData( [123, 456] );
Works in any context - both from a backend module or scheduler/CLI job, as well as in the frontend context, e.g. in the controller or a ViewHelper.
Absolute URLs are generated from the backend context into the frontend. The URLs are encoded as readable URLs - the slug path or RealURL are taken into account.
Get PID of the current page.
In the frontend: The current TSFE->id
In the backend: The page that was selected in the page tree
Without context: The pid of the site root
Parse list with 'ControllerName' => 'action,list,show'
Always specify the full class path in the ::class notation.
Take into account that before Typo3 10 only the simple class name (e.g. Main)
is used as the key.
Register a plugin for selection via the dropdown CType in the backend.
Use in Configuration/TCA/Overrides/tt_content.php â or ext_tables.php (deprecated).
Save a value in the sys_registry table.
Data in this table is retained beyond the session.
For example, a scheduler job can save when it was last executed.
was executed.
Arrays are recursively merged / merged by default:
Parse list with 'ControllerName' => 'action,list,show'
Always specify the full class path in the ::class notation.
Take into account that before Typo3 10 only the simple class name (e.g. Main)
is used as the key.
Register a plugin for selection via the dropdown CType in the backend.
Use in Configuration/TCA/Overrides/tt_content.php â or ext_tables.php (deprecated).
Save a value in the sys_registry table.
Data in this table is retained beyond the session.
For example, a scheduler job can save when it was last executed.
was executed.
Arrays are recursively merged / merged by default:
\nn\t3::Request()->GET( 'https://...', ['a'=>'123'] );
\nn\t3::Request()->GET( 'https://...', ['a'=>'123'], ['Accept-Encoding'=>'gzip, deflate'] );
// if 'a'=>[1,2,3] should be sent as a=1&a=2&a=3 instead of a[]=1&a[]=2&a[]=3
\nn\t3::Request()->GET( 'https://...', ['a'=>[1,2,3]], [], true );
Read the JWT (Json Web Token) from the request, validate it and, if the signature is
successfully check the signature and return the payload of the JWT.
\nn\t3::Request()->GET( 'https://...', ['a'=>'123'] );
\nn\t3::Request()->GET( 'https://...', ['a'=>'123'], ['Accept-Encoding'=>'gzip, deflate'] );
// if 'a'=>[1,2,3] should be sent as a=1&a=2&a=3 instead of a[]=1&a[]=2&a[]=3
\nn\t3::Request()->GET( 'https://...', ['a'=>[1,2,3]], [], true );
Read the bearer header
Is used, among other things, to transmit a JWT (Json Web Token).
\nn\t3::Request()->getBearerToken();
Copied!
| @return string|null
Source Code
publicfunctiongetBearerToken(){
$headers = $this->getAuthorizationHeader();
if (!empty($headers)) {
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
return $matches[1];
}
}
returnnull;
}
Copied!
Request::getJwt()
\nn\t3::Request()->getJwt();
Read the JWT (Json Web Token) from the request, validate it and, if the signature is
successfully check the signature and return the payload of the JWT.
Get extension configuration.
come from the LocalConfiguration.php, are defined via the extension settings
defined in the backend or ext_conf_template.txt
Get complete TypoScript via the Configuration Manager.
A simple wrapper for the core function but with try { ... } catch()
Fallback.
Does not work in every context - e.g. not in the CLI context!
Better: \nn\t3::Settings()->parseTypoScriptForPage(); use.
Returns the notation with dots. This can be done via
| \nn\t3::TypoScript()->convertToPlainArray() into a normal array
be converted into a normal array.
Get merge from TypoScript setup for a plugin and its flexform.
Returns the TypoScript array from plugin.tx_extname.settings... back.
Important: Only specify $extensionName if the setup of a FREMDEN extension
is to be fetched or there is no controller context because the
call is made from the backend... otherwise the FlexForm values are not taken into account!
In the FlexForm ``` use!
| ```` Then overwrite ``settings.varName` in the TypoScript setup
| $ttContentUidOrSetupArray can be the uid of a tt_content content element
or a simple array to overwrite the values from the TypoScript / FlexForm
\nn\t3::Settings()->getPlugin('extname') returns TypoScript from plugin.tx_extname...
Copied!
Important: Only specify $extensionName if the setup of a FREMDEN extension
is to be fetched or there is no controller context because the call is made e.g.
is made from the backend
Get site configuration.
This is the configuration that has been defined in the YAML files in the /sites folder since TYPO3 9.
Some of the settings can also be set via the "Sites" page module.
In the context of a MiddleWare, the site may not yet be parsed / loaded.
In this case, the $request from the MiddleWare can be passed to determine the site.
Get current (FIRST) StoragePid for the current plugin.
Saved in the TypoScript setup of the extension under
| plugin.tx_extname.persistence.storagePid or in the
FlexForm of the plugin on the respective page.
IMPORTANT: Merge with selected StoragePID from the FlexForm
only happens if $extNameis left empty.
Get ALL storagePids for the current plugin.
Saved as a comma-separated list in the TypoScript setup of the extension under
| plugin.tx_extname.persistence.storagePid or in the
FlexForm of the plugin on the respective page.
IMPORTANT: Merge with selected StoragePID from the FlexForm
only happens if $extNameis left empty.
Also get the child-PageUids?
| true takes the value for "Recursive" from the FlexForm or from the
TypoScript of the extension of plugin.tx_extname.persistence.recursive
Returns the notation with dots. This can be done via
| \nn\t3::TypoScript()->convertToPlainArray() into a normal array
be converted into a normal array.
// Get TypoScript for current pageUid
\nn\t3::Settings()->parseTypoScriptForPage();
// Get TypoScript for specific pageUid
\nn\t3::Settings()->parseTypoScriptForPage(123);
Write extension configuration.
Writes an extension configuration in the LocalConfiguration.php. The values can also be
corresponding configuration in ext_conf_template.txt, the values can also be edited via the Extension Manager / the
Extension configuration in the backend.
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 = '');
Get extension configuration.
come from the LocalConfiguration.php, are defined via the extension settings
defined in the backend or ext_conf_template.txt
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;
}
Get complete TypoScript via the Configuration Manager.
A simple wrapper for the core function but with try { ... } catch()
Fallback.
Does not work in every context - e.g. not in the CLI context!
Better: \nn\t3::Settings()->parseTypoScriptForPage(); use.
Returns the notation with dots. This can be done via
| \nn\t3::TypoScript()->convertToPlainArray() into a normal array
be converted into a normal array.
Get merge from TypoScript setup for a plugin and its flexform.
Returns the TypoScript array from plugin.tx_extname.settings... back.
Important: Only specify $extensionName if the setup of a FREMDEN extension
is to be fetched or there is no controller context because the
call is made from the backend... otherwise the FlexForm values are not taken into account!
In the FlexForm ``` use!
| ```` Then overwrite ``settings.varName` in the TypoScript setup
| $ttContentUidOrSetupArray can be the uid of a tt_content content element
or a simple array to overwrite the values from the TypoScript / FlexForm
\nn\t3::Settings()->getPlugin('extname') returns TypoScript from plugin.tx_extname...
Copied!
Important: Only specify $extensionName if the setup of a FREMDEN extension
is to be fetched or there is no controller context because the call is made e.g.
is made from the backend
| @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}"] ?? [];
}
Get site configuration.
This is the configuration that has been defined in the YAML files in the /sites folder since TYPO3 9.
Some of the settings can also be set via the "Sites" page module.
In the context of a MiddleWare, the site may not yet be parsed / loaded.
In this case, the $request from the MiddleWare can be passed to determine the site.
Get current (FIRST) StoragePid for the current plugin.
Saved in the TypoScript setup of the extension under
| plugin.tx_extname.persistence.storagePid or in the
FlexForm of the plugin on the respective page.
IMPORTANT: Merge with selected StoragePID from the FlexForm
only happens if $extNameis left empty.
Get ALL storagePids for the current plugin.
Saved as a comma-separated list in the TypoScript setup of the extension under
| plugin.tx_extname.persistence.storagePid or in the
FlexForm of the plugin on the respective page.
IMPORTANT: Merge with selected StoragePID from the FlexForm
only happens if $extNameis left empty.
Also get the child-PageUids?
| true takes the value for "Recursive" from the FlexForm or from the
TypoScript of the extension of plugin.tx_extname.persistence.recursive
Returns the notation with dots. This can be done via
| \nn\t3::TypoScript()->convertToPlainArray() into a normal array
be converted into a normal array.
// Get TypoScript for current pageUid
\nn\t3::Settings()->parseTypoScriptForPage();
// Get TypoScript for specific pageUid
\nn\t3::Settings()->parseTypoScriptForPage(123);
Write extension configuration.
Writes an extension configuration in the LocalConfiguration.php. The values can also be
corresponding configuration in ext_conf_template.txt, the values can also be edited via the Extension Manager / the
Extension configuration in the backend.
Get the entire SysCategory tree (as an array).
Each node has the attributes 'parent' and 'children' in order to
recursively iterate through the tree.
// Get the entire tree
\nn\t3::SysCategory()->getTree();
// Get a specific branch of the tree
\nn\t3::SysCategory()->getTree( $uid );
// Get all branches of the tree, key is the UID of the SysCategory
\nn\t3::SysCategory()->getTree( true );
Get the entire SysCategory tree (as an array).
Each node has the attributes 'parent' and 'children' in order to
recursively iterate through the tree.
// Get the entire tree
\nn\t3::SysCategory()->getTree();
// Get a specific branch of the tree
\nn\t3::SysCategory()->getTree( $uid );
// Get all branches of the tree, key is the UID of the SysCategory
\nn\t3::SysCategory()->getTree( true );
Add a selection option in the page properties under "Behavior -> Contains extension".
Traditionally used in Configuration/TCA/Overrides/pages.php, previously in ext_tables.php
// Register the icon in ext_localconf.php (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('description', 'identifier', 'icon-identifier');
Get default configuration for various typical types in the TCA
Serves as a kind of alias to write the most frequently used config arrays faster and
and to be able to write them more quickly
Standard config incl. image cropper, link and alternative image title
This setting changes regularly, which is quite an imposition given the number of parameters
and their changing position in the array is quite an imposition.
Inserts options from TypoScript into a TCA for selection.
Alias to nnt3::Flexform->insertOptions( $config, $a = null );
Description and further examples there.
Overwrite a configuration of the TCA, e.g. to overwrite a mask field with its own renderType
or to change core settings in the TCA on the pages or tt_content tables.
The following example sets/overwrites the config array in the TCA under:
See also \nn\t3::TCA()->setContentConfig() for a short version of this method when it comes to
the table tt_content and \nn\t3::TCA()->setPagesConfig() for the table pages
Add a selection option in the page properties under "Behavior -> Contains extension".
Traditionally used in Configuration/TCA/Overrides/pages.php, previously in ext_tables.php
// Register the icon in ext_localconf.php (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('description', 'identifier', 'icon-identifier');
Get default configuration for various typical types in the TCA
Serves as a kind of alias to write the most frequently used config arrays faster and
and to be able to write them more quickly
Standard config incl. image cropper, link and alternative image title
This setting changes regularly, which is quite an imposition given the number of parameters
and their changing position in the array is quite an imposition.
Inserts options from TypoScript into a TCA for selection.
Alias to nnt3::Flexform->insertOptions( $config, $a = null );
Description and further examples there.
Overwrite a configuration of the TCA, e.g. to overwrite a mask field with its own renderType
or to change core settings in the TCA on the pages or tt_content tables.
The following example sets/overwrites the config array in the TCA under:
See also \nn\t3::TCA()->setContentConfig() for a short version of this method when it comes to
the table tt_content and \nn\t3::TCA()->setPagesConfig() for the table pages
\nn\t3::Tsfe()->cObjData( $this->request ); => array with DB-row of the current content element
\nn\t3::Tsfe()->cObjData( $this->request, 'uid' ); => uid of the current content element
Inject fully initialized TypoScript into the request.
This is necessary when executing in a cached frontend context
in which the TypoScript setup array is not initialized. It uses the
TypoScriptHelper to create a complete TypoScript object and place it
into the frontend.typoscript attribute of the request.
// In the middleware:
$request = \nn\t3::Tsfe()->injectTypoScript( $request );
"Soft" variant: Uses a fake USER_INT object so that already rendered elements
elements do not have to be rendered again. Workaround for TYPO3 v12+, since
TypoScript Setup & Constants are no longer initialized when page is
completely loaded from the cache.
\nn\t3::Tsfe()->cObjData( $this->request ); => array with DB-row of the current content element
\nn\t3::Tsfe()->cObjData( $this->request, 'uid' ); => uid of the current content element
Copied!
| @return mixed
Source Code
publicfunctioncObjData( $request = null, $var = null ){
if (is_string($request)) {
$var = $request;
$request = null;
}
if (!$request) {
$request = \nn\t3::Environment()->getRequest();
}
if (!$request) {
\nn\t3::Exception('
\nn\t3::Tsfe()->cObjData() needs a $request as first parameter.
In a Controller-Context use \nn\t3::Tsfe()->cObjData( $this->request ).
For other contexts see here: https://bit.ly/3s6dzF0');
}
$cObj = $this->cObj( $request );
if (!$cObj) returnfalse;
return $var ? ($cObj->data[$var] ?? null) : ($cObj->data ?? []);
}
Inject fully initialized TypoScript into the request.
This is necessary when executing in a cached frontend context
in which the TypoScript setup array is not initialized. It uses the
TypoScriptHelper to create a complete TypoScript object and place it
into the frontend.typoscript attribute of the request.
// In the middleware:
$request = \nn\t3::Tsfe()->injectTypoScript( $request );
"Soft" variant: Uses a fake USER_INT object so that already rendered elements
elements do not have to be rendered again. Workaround for TYPO3 v12+, since
TypoScript Setup & Constants are no longer initialized when page is
completely loaded from the cache.
Only fetch annotations that are in a specific namespace.
In this example, only annotations that begin with @nn\rest
are fetched, e.g. @nn\rest\access ...
Only fetch annotations that are in a specific namespace.
In this example, only annotations that begin with @nn\rest
are fetched, e.g. @nn\rest\access ...
Various methods for parsing PHP source code and comments in the source code
source code (annotations). Objective: To create automated documentation from the comments
in the PHP code.
Examples for the use incl. rendering of the template
Parses the annotation above the class definition and optionally also all methods of the class.
Returns an array where the arguments / parameters of each method are also listed.
Markdown can be used in the annotations, the Markdown is automatically converted to HTML code.
Parse a folder (recursively) for classes with annotations.
Returns an array with information about each class and its methods.
The annotations (comments) above the class methods can be formatted in Markdown, they are automatically converted to HTML with appropriate `` and tags are converted.``
Parses the annotation above the class definition and optionally also all methods of the class.
Returns an array where the arguments / parameters of each method are also listed.
Markdown can be used in the annotations, the Markdown is automatically converted to HTML code.
Parse a folder (recursively) for classes with annotations.
Returns an array with information about each class and its methods.
The annotations (comments) above the class methods can be formatted in Markdown, they are automatically converted to HTML with appropriate `` and tags are converted.``
The helper makes it possible to use the JavaScript object notation in TypoScript and to convert it into an array via the {nnt3:parse.json()} ViewHelper.
This is practical if, for example, slider configurations or other JavaScript objects are to be defined in TypoScript in order to use them later in JavaScript.
Another application example: You want to use the "normal" JS syntax in a .json file instead of the JSON syntax.
Let's take a look at an example. This text was written in a text file and is to be parsed via PHP:
// Contents of a text file.
{
example: ['one', 'two', 'three']
}
Copied!
PHP would report an error in this example with json_decode(): The string contains comments, wrapping and the keys and values are not enclosed in double quotes. However, the JsonHelper or the ViewHelper $jsonHelper->decode() can easily convert it.
This is how you could define a JS object in the TypoScript setup:
// Content in the TS setup
my_conf.data (
{
dots: true,
sizes: [1, 2, 3]
}
)
Copied!
The mixture is a little confusing: my_conf.data (...) opens a section in the TypoScript for multi-line code.
There is then a "normal" JavaScript object between the (...)
This can then simply be used as an array in the Fluid template:
This script is mainly based on the work of https://bit.ly/3eZuNu2 and
has been optimized by us for PHP 7+.all credit and glory please in this direction.
The PHP function json_decode() only works with JSON syntax: {"key": "value"}. Neither line breaks nor comments are allowed in JSON.
This function can also be used to parse strings in JavaScript notation.
The PHP function json_decode() only works with JSON syntax: {"key": "value"}. Neither line breaks nor comments are allowed in JSON.
This function can also be used to parse strings in JavaScript notation.
| @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);
Converts a variable into JSON format.
Relic of the original class, probably from a time when json_encode() did not yet exist.
In order to use this function, a Deep-L API key must be stored in the nnhelpers extension manager.
The key is free of charge and allows the translation of 500,000 characters per month.
// Activate translator
$translationHelper = \nn\t3::injectClass( \Nng\Nnhelpers\Helpers\TranslationHelper::class );
// Allow translation via Deep-L
$translationHelper->setEnableApi( true );
// Set target language
$translationHelper->setTargetLanguage( 'EN' );
// Allow max. Allow max. number of translations (for debugging purposes)
$translationHelper->setMaxTranslations( 2 );
// Path in which the l18n files should be saved / cached
$translationHelper->setL18nFolderpath( 'EXT:nnhelpers/Resources/Private/Language/' );
// Start translation
$text = $translationHelper->translate('my.example.key', 'This is the text to be translated');
Generates a unique hash / checksum from the text.
The transferred text is always the base language. If the text in the base language changes, the method returns a different checksum.
This recognizes when a text needs to be retranslated. Pure changes to whitespaces and tags are ignored.
Sets the current folder in which the translation files are cached.
The idea is to translate the translated texts for backend modules only once and then save them in the extension folder.
From there they are then deployed to GIT.
Sets the maximum number of translations to be made per instance.
Helps with debugging (so that the Deep-L quota is not exhausted by testing) and with TimeOuts if many texts need to be translated.
$translationHelper->setMaxTranslations( 5 ); // Abort after 5 translations
$translationHelper = \nn\t3::injectClass( \Nng\Nnhelpers\Helpers\TranslationHelper::class );
$translationHelper->setEnableApi( true );
$translationHelper->setTargetLanguage( 'EN' );
$text = $translationHelper->translate('my.example.key', 'This is the text to be translated');
Generates a unique hash / checksum from the text.
The transferred text is always the base language. If the text in the base language changes, the method returns a different checksum.
This recognizes when a text needs to be retranslated. Pure changes to whitespaces and tags are ignored.
Sets the current folder in which the translation files are cached.
The idea is to translate the translated texts for backend modules only once and then save them in the extension folder.
From there they are then deployed to GIT.
Sets the maximum number of translations to be made per instance.
Helps with debugging (so that the Deep-L quota is not exhausted by testing) and with TimeOuts if many texts need to be translated.
$translationHelper->setMaxTranslations( 5 ); // Abort after 5 translations
$translationHelper = \nn\t3::injectClass( \Nng\Nnhelpers\Helpers\TranslationHelper::class );
$translationHelper->setEnableApi( true );
$translationHelper->setTargetLanguage( 'EN' );
$text = $translationHelper->translate('my.example.key', 'This is the text to be translated');
Copied!
| @return string
Source Code
publicfunctiontranslate( $key, $text = '' ){
$keyHash = $this->createKeyHash( $key );
$textHash = $this->createTextHash( $text );
$l18nData = $this->loadL18nData();
$translation = $l18nData[$keyHash] ?? ['_cs'=>false];
$textChanged = $translation['_cs'] != $textHash;
$autoTranslateEnabled = $this->enableApi && ($this->maxTranslations == 0 || $this->maxTranslations > $this->numTranslations );
// Text wurde übersetzt und hat sich nicht geändertif (!$textChanged) {
$str = $translation['text'];
$str = str_replace('.</p>.', '.</p>', $str);
return $str;
}
// Text wurde nicht übersetzt und Deep-L Übersetzung ist deaktiviertif (!$autoTranslateEnabled) {
if ($translation['_cs'] !== false) {
return"[Translation needs {$this->targetLanguage} update] " . $text;
}
return"[Translate to {$this->targetLanguage}] " . $text;
}
$this->numTranslations++;
echo"Translating via Deep-L: {$this->numTranslations} / {$this->maxTranslations} [$keyHash] " . json_encode($key) . "\n";
$result = \nn\t3::LL()->translate( $text, $this->targetLanguage );
$l18nData[$keyHash] = [
'_cs' => $textHash,
'text' => $result,
];
$this->saveL18nData( $l18nData );
return $result;
}
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
This ViewHelper is not a separate ViewHelper that can be used in Fluid.
It serves as a base class for your own ViewHelper.
| $escapeOutput = false is set as the default.
If XSS attacks could be a problem with your ViewHelper, this should be overwritten.
Use extend in your own ViewHelper to use it.
Here is an example boilerplate with everything you need to get started:
registerArgument('title', 'string', 'info', false);
}
publicstaticfunctionrenderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext ){
// Simply use `$title` instead of `$arguments['title']`foreach ($arguments as $k=>$v) {
${$k} = $v;
}
// Render content between the ViewHelper tagif (!$title) $title = $renderChildrenClosure();
// Example to get all current variables in the Fluid template// $templateVars = \nn\t3::Template()->getVariables( $renderingContext );return $title;
}
}
Copied!
abstractTagBased
Description
<nnt3:abstractTagBased />
This ViewHelper is not a separate ViewHelper that can be used in Fluid.
It serves as a base class for your own tag-based ViewHelper.
Use extend in your own ViewHelper to use it.
Here is an example boilerplate with everything you need to get started:
Loads the raw data of a column (colPos) of the backend layout.
This is the raw tt_content-data array of a column (colPos) from the backend layout.
By default, the relations (FAL, assets, media...) are also loaded. Can be prevented via relations:0.
Replace variables in the rendered content element.
Allows you to create a content element in the backend that works with fluid variables - e.g. for a mail template where the recipient's name should appear in the text.
Helpful if, for example, an email is to be sent with a confirmation link.
The UID of the data record is also transferred as a hash. The controller then checks
whether the hash can be generated from the passed uid
If not, the uid has been manipulated.
Masks "critical" characters so that they can be used as an attribute to an HTML tag.
...
...
Copied!
| @return string
format.code
Description
<nnt3:format.code />
Highlighted code sections via PrismJS.
The code can be made available for direct download via download.
The file is generated dynamically via JS and streamed - no additional files are created on the server.
nnhelpers uses this function to offer the boilerplates as a download.
Does not throw an error if no image or src was passed.
Also allows the transfer of an array, simply pulls the first image.
// tt_content.image is actually an array. It simply renders the first image!
{nnt3:image(image:data.image)}
// does not throw an error (if the editor has not uploaded an image!)
{nnt3:image(image:'')}
Copied!
link.cloneRecord
Description
<nnt3:link.cloneRecord />
Generate link to clone a data record for a backend module.
Converts a normal JavaScript object that is passed as a string into an array.
Allows you to create configurations for sliders and other JS libraries in TypoScript and parse them later via JS.
See JsonHelper for examples.
{myConfig->nnt3:parse.json()->f:debug()}
Copied!
...
Copied!
| @return mixed
parse.markdown
Description
<nnt3:parse.markdown />
Converts a string to HTML with Markdown.
{myMarkdownCode->nnt3:parse.markdown()}
Copied!
| @return mixed
translate
Description
<nnt3:translate />
Translate a text, including optional translation via Deep-L.
See also the TranslationHelper documentation for integration via PHP or a controller.
// Translation via locallang.xlf
{mytext->nnt3:translate(id:'LLL:EXT:nnaddress/Resources/Private/Language/locallang_db.xlf:my-ll-id')}
{mytext->nnt3:translate(id:'my-ll-id', extensionName:'nnaddress')}
Useful e.g. for unique IDs or class names in Fluid templates.
{nnt3:uniqid()}
Copied!
...
Copied!
| @return string
uri.image
Description
<nnt3:uri.image />
Simplifies the use of the Uri.ImageViewhelper.
Does not throw an error if no image or src was passed.
Also allows the transfer of an array, simply pulls the first image.
// tt_content.image is actually an array. It simply renders the first image!
{nnt3:uri.image(image:data.image)}
// does not throw an error (if the editor has not uploaded an image!)
{nnt3:uri.image(image:'')}
Copied!
uri.page
Description
<nnt3:uri.page />
Generates a URL to a page in the frontend.
Corresponds almost exactly to the Typo3 ViewHelper {f:uri.page()} - but can also be used in a context
context where no frontend(TSFE) exists, e.g. in the template of a backend module or in the mail template of ascheduler job.
mail templates of a scheduler job.
You might think: The Typo3 DebuggerUtility is great. Yes, it is.
But have you ever tried to debug a QueryBuilder-Statment? Or have you tried to find a debug command somewhere in your code that you forgot to remove and you can't remember in which class and method you had put it?
Here is the one thing that will get you addicted to nnhelpers. Even if you ignore the rest and go your own way. This is worth it:
\nn\t3::debug( $whatever );
Copied!
Creating Extensions
Many small things make you wonder while you develop Typo3 extensions.
One of them is: When Typo3 switched to the IconRegistry way of registering icons in ext_tables.php – why do you have to first use the correct instance of the {Type}IconProvider to get the icon in its place? To much brain work.
So, here you go. Stop asking Google. Ask nnhelpers:
Another one of my favorites: Defining FALs in the TCA.
Remember the 28 lines of code for enabling the file select/upload in the TCA? Remember having to slightly change the syntax and structure with (almost) every major Typo3 update?
Well, here is your time-saver for your next FAL definition in the TCA:
And of course, there are lots more, like a oneliner for a Rich Text Editor (RTE).
If the core team decides to move away from ckeditor like they (fortunately) did with rtehtmlarea, then it won't be your problem anymore. It will be nnhelpers problem.
Might sound crazy, but we actually need this all the time when extending the best extension ever developed for Typo3: Mask (EXT:mask).
In the following example, we had about 30 slider-options for transition-types, duration, resposiveness etc. Every option is selectable in the plugin by the user. With plain old mask this would mean extending the tt_content-table by 30 fields. Fields, that have no other logic connected to them(searchability, indexing, sorting etc.). A one-time-shot for rendering the content-element – and therefore a clear case for a FlexForm.
Mask doesn't allow injecting a FlexForm (yet). So here is what we do in Configuration/TCA/Overrides/tt_content.php. (Make sure to define a dependency from your extension to mask!)
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 pimping
One thing we love to do is make the values in a FlexForm configurable over TypoScript Setup oder the pageTSConfig, e.g. to let the user see different options in the dropdown of a flexform (or TCA), depending on the rootline he is on in the backend.
Look at this nice Helper:
<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!
Ah, right. And then there was that thing about inserting a select-field for all countries.
Great. Typo3 just removed SwiftMailer.
We went through this already a few years ago, when Typo3 switched TO SwiftMailer. We have the mail-function scattered over 562 extensions. Thanks.
And, nope, sorry, we DON'T spend hours reading the breaking changes everytime we have to do an update. We upgrade the core, put on our helmets and safety-belts and see what happens. Google will somehow cut us out of the accident scene.
But, right, then there was that thing about setting the partialRootPaths, layoutRootPaths etc. As you probably have noticed yourself, most of the time, you are in an extension when using the StandaloneView. And most of the time you want to use the view settings defined in the TypoScript-Setup for this extension.
Let's put it this way: There is no wrong way to use nnhelpers.
Think simple and intuitive. Do what seems logical to you.
Most of the time, we will have had the same idea.
Importing data from one extension to another
We recently updated a major project from Typo3 7 LTS to Typo3 10 LTS. The project used Calendar Base (EXT:cal) and had over 5.000 calender-entries. Unfortunatly EXT:cal had not been updated to work with Typo3 10 so we decided to switch to our own calendar extension nncalendar (which will be released for public in a few weeks).
But here we faced three main challenges:
It was impossible to activate EXT:cal in Typo3 10 - consequently there was no simple way to access the database-tables of Calendar Base or create "nice" Models with getters and setters
Calendar Base hat not migrated their calendar categories to the sys_category yet.
There were tons of raw images in the uploads/pic/ folder which needed to be converted to FAL images and attached to the new EntryModel of nncalendar
Here is the essence of what we came up with:
// Get all rows as array from the Calendar Base table. EXT:cal does NOT need to activated!
$calData = \nn\t3::Db()->statement( "SELECT * FROM tx_cal_event WHERE deleted = 0");
// This is the Repository we're aiming at
$calendarRepository = \nn\t3::injectClass(\Nng\Nncalendar\Domain\Repository\EntryRepository::class);
// Create NnCalendar-Models from the raw array-dataforeach ($calData as $row) {
// [...] we had a few lines of code here for parsing and converting the date etc.
$entry = \nn\t3::Convert($row)->toModel( \Nng\Nncalendar\Domain\Model\Entry::class );
$calendarRepository->add( $entry );
}
\nn\t3::Db()->persistAll();
Copied!
Even setting the new SysCategories in the new Model was as simple as:
nnhelpers automatically recognizes, that the Entry-Model has SysCategories related to the field category and will create the according relations and ObjectStorage on-the-fly.
nnhelpers automatically recognizes, that falImage is defined as a FAL or ObjectStorage in the Entry-Model and creates the sys_file and sys_file_reference which it then attaches to the model.
So, what are YOU going to do the rest of the day?
Database operations
I can't remember how many times we just wanted to do a direct and straightforward update, delete or insert of individual records in a database table - without digging through the docs again and again.
Here is a small excerpt from the nn\t3::Db() methods that save us time every day:
Get data for the FrontendUser with uid = 12
$feUser = \nn\t3::Db()->findByUid('fe_user', 12);
Copied!
Ignore the enable-fields (hidden, start_time etc.)
Still not believing, that this Extension could completely change the way you have been working on Typo3 projects up until now?
Let's have a look at some of the everyday tasks you have been implementing over and over again.
Compare not only the number of code-lines involved in getting the job done – but also the brain-energy needed to memorize
the steps, methods and parameters required in the direct comparison.
Also pay attention to how the main concept of building links in the backend context has changed from Version 8 to 9.
Sure: Things have definately gotten better. But think of the time you would have spent to find this solution and update it in all of
you extensions.
Then look at the nnhelpers one-liner on the right side. See the difference between building links in the frontend or backend context?
Any difference in building the links from version 8 LTS to 9 LTS?
I think you understand, what we are talking about.
Getting the TypoScript Setup outside of the Controller
Task: You need to get the demo.path value from the TypoScript setup of a plugin, but you are not in a context where you could simply do a $this->settings to retrieve it.
Build a Link - in the Backend Context, prior Version 9 LTS
Task: Create a Link from a scheduler or CLI Job and pass a parameter.
This script could be in a task called by a crobjob via Scheduler.
Up until version Typo3 9 LTS it was necessary to manually init the Frontend.
Build a Link - in the Backend Context, since Version 9 LTS
Same task: Create a Link from a scheduler or CLI Job and pass a parameter.
This script could be in a task called by a crobjob via Scheduler.
With version 9 of Typo3 things have gotten a little more easy... or have they?
Task: Get the raw data of a database table and ignore the hidden field and start/endtime restrictions i.e. to simply export it as CSV or iterate through it.
We are currently translating all the comments, annotations and descriptions in to English.
Update coming soon!
Many, many time-savers
Quick search function
View source code in backend
Download Boilerplates
Smart namespacing
Change log
Version 1.0.0
First public release after many years of development and improvement.
Known Problems
Unfortunalety tons of problems related to the big, red warning on this page
Helpers for WordPress
We have a vision.
What if there was a similar collection of "helpers" for other content management systems? If the methods were even
(almost) congruent? If a developer could simply "jump" between Joomla, WordPress, Drupal, NodeJS and TYPO3
without having to learn other concepts and core APIs over and over again.
This is the idea behind nnhelpers - with the long term goal to make even a large part of the code reusable between different
CMS. Sure: This is a dream and it comes with hurdles. But just knowing: There are
\nn\t3::debug() or \nn\t3::Db()->findAll() or \nn\t3::Environment()->getBaseUrl() - and this command is
framework-spanning the same would already be a big help. No matter if you really want to implement nnhelpers then - or simply
just use it as a cheat sheet to see how the function is implemented in detail within the respective system.
We set a starting point in 2022 and started to bring wphelpers to life: A mirror of nnhelpers
for WordPress!
What was one of our first steps and methods of wphelpers? Getting a decent template engine up and running.
WordPress relies on PHP-templating - from the point of view of a Fluid- or Twig-accustomed developer this feels like a
anachronistic disaster.
With wphelpers you can now use this nice line within your WordPress plugin:
... and use it to render a fluid template! Thanks to the community who made Fluid available as a standalone version.
And since Fluid is still one of the best template engines out there - why not "upgrade" WordPress with it?
Now all templates of the TYPO3 extensions are reusable in WordPress!
And the performance? WordPress always argues that nothing is more performant than a PHP template. But if you know Fluid in depth,
you knows that all templates are "translated" into pure PHP code and cached (same with Smarty etc.). So there will be
hardly any difference in performance.
Let's do it!
If there are other teams out there, who move in the parallel universes between TYPO3, WordPress etc. and
find this idea interesting: What do you think about it? Feel like getting in on the action? Do you want to translate a method from nnhelpers into another system?
into another system? Let's start the revolution ;)
We are looking forward to your 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.