Extending Routing

The TYPO3 Routing is extendable by design, so you can write both custom aspects as well as custom enhancers.

  • You should write a custom enhancer if you need to manipulate how the full route looks like and gets resolved.
  • You should write a custom aspect if you want to manipulate how a single route parameter ("variable") gets mapped and resolved.

Writing custom aspects

Custom aspects can either be modifiers or mappers. A modifier provides static modifications to a route path based on a given context (for example "language"). A mapper provides a mapping table (either a static table or one with dynamic values from the database).

All aspects derive from the interface \TYPO3\CMS\Core\Routing\Aspect\AspectInterface .

To write a custom modifier, your aspect has to extend \TYPO3\CMS\Core\Routing\Aspect\ModifiableAspectInterface and implement the modify method (see \TYPO3\CMS\Core\Routing\Aspect\LocaleModifier as example).

To write a custom mapper, your aspect should either implement \TYPO3\CMS\Core\Routing\Aspect\StaticMappableAspectInterface or \TYPO3\CMS\Core\Routing\Aspect\PersistedMappableAspectInterface , depending on whether you have a static or dynamic mapping table. The latter interface is used for mappers that need more expensive - for example database related - queries as execution is deferred to improve performance.

All mappers need to implement the methods generate and resolve. The first one is used on URL generation, the second one on URL resolution.

After implementing the matching interface, your aspect needs to be registered in ext_localconf.php:

EXT:my_extension/ext_localconf.php
<?php

declare(strict_types=1);

use MyVendor\MyExtension\Routing\Aspect\MyCustomMapper;

defined('TYPO3') or die();

$GLOBALS['TYPO3_CONF_VARS']['SYS']['routing']['aspects']['MyCustomMapperNameAsUsedInYamlConfig'] =
    MyCustomMapper::class;
Copied!

It can now be used in the routing configuration as type. The example above could be used as type: MyCustomMapperNameAsUsedInYamlConfig.

If your aspect is language aware, it should additionally implement SiteLanguageAwareInterface with the methods setSiteLanguage(Entity\SiteLanguage $siteLanguage) and getSiteLanguage(). setSiteLanguage will automatically be called with the current site language object.

Impact

Routing aspects respecting the site language are now using the SiteLanguageAwareInterface in addition to the SiteLanguageAwareTrait. The AspectFactory check has been adjusted to check for the interface _or_ the trait. If you are currently using the trait, you should implement the interface as well.

Writing custom enhancers

Enhancers can be either decorators or routing enhancers providing variants for a page.

The interfaces contain methods you need to implement as well as a description of what the methods are supposed to do. Please take a look there.

To register the enhancer, add the following to your ext_localconf.php:

EXT:my_extension/ext_localconf.php
<?php

declare(strict_types=1);

use MyVendor\MyExtension\Routing\Enhancer\MyCustomEnhancer;

defined('TYPO3') or die();

$GLOBALS['TYPO3_CONF_VARS']['SYS']['routing']['enhancers']['MyCustomEnhancerAsUsedInYaml']
    = MyCustomEnhancer::class;
Copied!

Now you can use your new enhancer in the routing configuration as type. The example above could be used as type: MyCustomEnhancerAsUsedInYaml.

Manipulating generated slugs

The "slug" TCA type includes a possibility to hook into the generation of a slug via custom TCA generation options.

Hooks can be registered via

$GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config']['generatorOptions']['postModifiers'][] = My\Class::class . '->method';
Copied!

in EXT:myextension/Configuration/TCA/Overrides/table.php, where $tableName can be a table like pages and $fieldName matches the slug field name, e.g. slug.

$GLOBALS['TCA']['pages']['columns']['slug']['config']['generatorOptions']['postModifiers'][] = My\Class::class . '->modifySlug';
Copied!

The method then receives an parameter array with the following values:

 [
     'slug' ... the slug to be used
     'workspaceId' ... the workspace ID, "0" if in live workspace
     'configuration' ... the configuration of the TCA field
     'record' ... the full record to be used
     'pid' ... the resolved parent page ID
     'prefix' ... the prefix that was added
     'tableName' ... the table of the slug field
     'fieldName' ... the field name of the slug field
];
Copied!

All hooks need to return the modified slug value.

Any extension can modify a specific slug, for instance only for a specific part of the page tree.

It is also possible for extensions to implement custom functionality like "Do not include in slug generation" as known from RealURL.