You are currently viewing TYPO3 Routing: Extensions and Enhancers

TYPO3 Routing: Extensions and Enhancers

The new routing feature of TYPO3 is one of the major achievements of the last TYPO3 LTS release. Routing for pages works out of the box, but the routing for extensions requires some manual configuration. This post gives an overview over the various possibilities.

The routing for pages is quite simple: TYPO3 suggests a slug build from the pagetree. If it does not fit for you, you can modify the complete URL segment.

Slugs, enhancers and aspects

But plugins need flexible arguments. They need to know what to display on the one side. On the other side TYPO3 must know how to create the speaking urls displayed on the website.

This flexibility is provided by the so called “Enhancers”. The configuration of these enhancers must be added manually to the section routeEnhancers: of the config.yaml of your site configuration. Currently there is no way to split the configuration to several files and to include it, like with the RTE configuration.

Enhancers provide the overall structure and the segments of an URL. The details, how a segment will look like is defined in the “Aspects”.

TYPO3 enhancers

TYPO3 comes with four enhancers out of the box:

  • SimpleEnhancer
  • PageTypeSuffixEnhancer
  • PluginEnhancerand the
  • ExtbasePluginEnhancer.

Configuration basics

Before I go into the details of the enhancers, let’s have a look at the configuration.

routeEnhancers:
  CategoryListing:
    type: Simple
    limitToPages: [13]
    routePath: '/show-by-category/{category_id}/{tag}'
    defaults:
      tag: ''
    requirements:
      category_id: '[0-9]{1..3}'
      tag: '^[a-zA-Z0-9].*$'

All configuration for the enhancers is in the section routeEnhancers. It resides on the root level of the config file, parallel to f.e. rootPageId or languages. The second line is a unique string, you can choose freely. The third line type defines the type of the enhancer. This can be any of the above types.

limitToPages is an array with page ids, where the routePath must be evaluated. This is optional, but increases the performance while creating the speaking urls. If The routePath defines how the url will finally look like. The defaults section provides the default values, if a variable part of the route path is omitted. requirements is an array where possible accepted values for the variables can be restricted.

PageTypeSuffix Enhancer

With the PageTypeSuffix Enhancer you can define, which page type should get a suffix. For example, if you defined a list view of plugin as a rss feed or a json view, you can map the type ids to certain suffixes.

routeEnhancers:
   PageTypeSuffix:
      type: PageType
      default: '.html'
      index: 'index'
      map:
         'rss.feed': 13
         '.json': 26

With the section map the suffixes of the url are mapped to the defined typeNum values in the TypoScript settings.

Simple Enhancer

The Simple Enhancer adds “simple” properties to a url.

routeEnhancers:
  CategoryListing:
    type: Simple
    limitToPages: [13]
    routePath: '/show-by-category/{category_id}/{tag}'
    defaults:
      tag: ''
    requirements:
      category_id: '[0-9]{1..3}'
      tag: '^[a-zA-Z0-9].*$'
    _arguments:
      category_id: 'category'

This configuration translates the url

index.php?id=13&category=241&tag=Benni to path-to/my-page/show-by-category/241/Benni. The arguments section is mapping the category_id to category.

Plugin Enhancer

The Plugin Enhancer works for piBased plugins. There is an additional setting in the routing configuration: namespace. This is the name of the plugin like tx_felogin_pi1 or tx_ttnews. A plugin enhancer will convert a “classic” url from

subpage-1/sub-sub-page?tx_ttnews[tt_news]=1&cHash=eb5a0688e4549b7c267365046f8215e0
to
subpage-1/sub-sub-page/1?cHash=eb5a0688e4549b7c267365046f8215e0

routeEnhancers:
  TtNews:
    type: Plugin
    limitToPages: [2,3]
    namespace: 'tx_ttnews'
    routePath: '/{tt_news}'
    requirements:
      tt_news: '[0-9]{1..5}'

Ok … agreed, that’s not very nice, but it can be beautified by an aspect. Aspects are the topic of the last part of this article.

ExtbasePlugin Enhancer

The Extbase Plugin Enhancer does the same as the normal plugin enhancer, but for extbase plugins. I took the following example right from the ChangeLog entry of TYPO3 9.

routeEnhancers:
  NewsPlugin:
    type: Extbase
    limitToPages: [13]
    extension: News
    plugin: Pi1
    routes:
      - { routePath: '/list/{page}', _controller: 'News::list', _arguments: {'page': '@widget_0/currentPage'} }
      - { routePath: '/tag/{tag_name}', _controller: 'News::list', _arguments: {'tag_name': 'overwriteDemand/tags'}}
      - { routePath: '/blog/{news_title}', _controller: 'News::detail', _arguments: {'news_title': 'news'} }
      - { routePath: '/archive/{year}/{month}', _controller: 'News::archive' }
    defaultController: 'News::list'
    defaults:
      page: '0'
    requirements:
      page: '\d+'

In this definition are three new attributes: extension, plugin and routes. The extension is the extension name and plugin the used plugin of the extension. These two values can be replaced by a namespace definition like in the plugin enhancer. In this case the namespace would be tx_news_pi1.

The routes segment can contain multiple route definitions. This is necessary, because an extbase plugin can and probably will have multiple actions. Each of them must have a representation in the route paths. Such a definition consists of three parts:

  • the routePath is the same, as we know it already from the plugin enhancer and the simple enhancer
  • the _controller part takes the one combination of a controller and an available action as defined in ext_localconf.php of the extension.
  • the _arguments section defines, as the name suggests, the arguments, which are necessary to find the right database record.

If none of the routes matches the defaultController is used.

So this definition will map the url index.php?id=13&tx_news_pi1[controller]=News&tx_news_pi1[action]=detail&tx_news_pi1[news]=13 to /path-to/my-page/detail/13.

Aspects

You might have noticed, that displayed url contains uids of a single record. This is were the aspects aka mappers come in. These are there to beautify exactly these values and other numeric

PersistedAliasMapper

The PersistedAliasMapper maps an uid to a certain field within that record, usually the slug field, formerly know as the “speaking url path segment”.

aspects:
      news_title:
        type: PersistedAliasMapper
        tableName: 'tx_news_domain_model_news'
        routeFieldName: 'path_segment'
        routeValuePrefix: '/'

The routeFieldName sets the column name, that is used. The value of the field must be unique within the site. If you use the TCA type slug in your extension, this eases the work of the TYPO3 integrators and editors. This TCA type takes care of the uniqueness and adds a number to the slug, in case it is not unique.

The routeValuePrefix is special for slug TCA fields. It defines how the path is connected to the root page. A single / removes everything up to the domain, but it can contain any string you want.

PersistedPatternMapper

The PersistedPatternMapper creates the speaking url path depending on several fields from a database record. This is useful, if a used field may be not unique. Adding the uid to the speaking url, makes it unique.

Here is an example to enhance the tt_news url from above:

routeEnhancers:
  TtNews:
    type: Plugin
    limitToPages: [2,3]
    namespace: 'tx_ttnews'
    routePath: '/{tt_news}'
    requirements:
      tt_news: '[0-9]{1..5}'
    aspects:
      tt_news:
        type: PersistedPatternMapper
        tableName: 'tt_news'
        routeFieldPattern: '^(?P<title>.+)-(?P<uid>\d+)$'
        routeFieldResult: '{title}-{uid}'

The aspect tt_news corresponds to the route path field {tt_news}. They must always have the same name. The attribute tableName contains the name of the database table, where the values are taken from. The routeFieldPattern defines how, the url is build from the database, the routeFieldResult shows, how the fields are rendered in the URL.

This definition will map subpage-1/sub-sub-page?tx_ttnews[tt_news]=1&cHash=eb5a0688e4549b7c267365046f8215e0 to subpage-1/sub-sub-page/Testnews-1

StaticValueMapper

Now we come to the easier mappers. A static value mapper replaces a defined value with another one. An example is the mapping of the month numbers with their “speaking” values: “10” becomes “Oktober”, “11” becomes “November” and so on. These mapping can also be localized to match the current language in the frontend. The following example is again from the TYPO3 changelog. The first part of the mapping will be shown in the url. the second one is the value coming from the controller.

routeEnhancers:
  NewsArchive:
    type: Extbase
    limitToPages: [13]
    extension: News
    plugin: Pi1
    routes:
      - { routePath: '/{year}/{month}', _controller: 'News::archive' }
    defaultController: 'News::list'
    defaults:
      month: ''
    aspects:
      month:
        type: StaticValueMapper
        map:
          january: 1
          february: 2
          march: 3
          april: 4
          may: 5
          june: 6
          july: 7
          august: 8
          september: 9
          october: 10
          november: 11
          december: 12
      localeMap:
        - locale: 'de_.*'
          map:
            januar: 1
            februar: 2
            maerz: 3
            april: 4
            mai: 5
            juni: 6
            juli: 7
            august: 8
            september: 9
            oktober: 10
            november: 11
            dezember: 12

LocaleModifier

The LocaleModifier “translates” parts of an url between the languages. This is useful if there are static strings in the url, that should look different in the various languages. An example can be a product database, like the following example:

routeEnhancers:
  NewsArchive:
    type: Extbase
    limitToPages: [13]
    extension: MyProducts
    plugin: Pi1
    routes:
      - { routePath: '/{localized_product}/{product}', _controller: 'MyProducts::detail' }
    defaultController: 'MyProducts::list'
    aspects:
      localized_product:
        type: LocaleModifier
        default: 'product'
        localeMap:
          - locale: 'fr_FR.*|fr_CA.*'
            value: 'produit'
          - locale: 'de_DE.*'
            value: 'produkt'
          - locale: 'es_ES.*'
            value: 'producto'

StaticRangeMapper

StaticRangeMappers are there to reduce the necessity of an ugly chash value appended to an url. A typical use case are fluid widgets and pagers. It is recommended to use it for paginations.

  aspects:
      page:
        type: StaticRangeMapper
        start: '1'
        end: '100'

The aspect defines an upper and lower constraints. If the value is outside, the “normal” url will be delivered to the browser.

Conclusion

It is really great to have also the routing aka speaking urls available from the TYPO3 core, dealing with most aspects for a website. All examples show only one plugin with different actions per page and you may ask: “What if I have different plugins on a page?”. This is where custom enhancers must be developed. I will cover this topic in the next months and give you an idea how to solve it.

Credits

I want to thank my supporters via patreon.com, who make this blog post possible. This week it is

Josef Glatz (@joushcom)

If you appreciate my blog and want to support me, you can say “Thank You!”. Find out the possiblities here:

I found the blog post image on pixabay . It was published by Free Photos under the CC0 public domain license. It was modified by myself using pablo on buffer.