1 .. include:: ../../Includes.txt
4 ===============================================
5 Feature: #86365 - Routing Enhancers and Aspects
6 ===============================================
13 Page-based routing is now flexible by adding enhancers to Routes that are generated or resolved with parameters, which
14 were previously appended as GET parameters.
16 An enhancer creates variants of a specific page-base route for a specific purpose (e.g. one plugin, one Extbase plugin)
17 and enhance the existing route path which can contain flexible values, so-called "placeholders".
19 On top, aspects can be registered to a specific enhancer to modify a specific placeholder, like static speaking names
20 within the route path, or dynamically generated.
22 To give you an overview of what the distinction is, we take a regular page which is available under
24 `https://www.example.com/path-to/my-page`
26 to access the Page with ID 13.
28 Enhancers are ways to extend this route with placeholders on top of this specific route to a page.
30 `https://www.example.com/path-to/my-page/products/{product-name}`
32 The suffix `/products/{product-name}` to the base route of the page is added by an enhancer. The placeholder variable
33 which is added by the curly braces can then be statically or dynamically resolved or built by an Aspect or more
34 commonly known a Mapper.
36 Enhancers and aspects are activated and configured in a site configuration, currently possible by modifying the
37 site's :file:`config.yaml` and adding the :yaml:`routeEnhancers` section manually, as there is no UI available for
38 this configuration. See examples below.
40 It is possible to use the same enhancers multiple times with different configurations, however, be aware that
41 it is not possible to combine multiple variants / enhancers that match multiple configurations.
43 However, custom enhancers can be built to overcome special use cases where e.g. two plugins with multiple parameters
44 each could be configured. Otherwise, the first variant that matches the URL parameters is used for generation and
50 TYPO3 comes with the following enhancers out of the box:
52 - Simple Enhancer (enhancer type "Simple")
53 - Plugin Enhancer (enhancer type "Plugin")
54 - Extbase Plugin Enhancer (enhancer type "Extbase")
56 Custom enhancers can be registered by adding an entry to an extensions :file:`ext_localconf.php`.
58 :php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['routing']['CustomPlugin'] = \MyVendor\MyPackage\Routing\CustomEnhancer::class;`
60 Within a configuration, an enhancer always evaluates the following properties:
62 * `type` - the short name of the enhancer as registered within :php:`$TYPO3_CONF_VARS`. This is mandatory.
63 * `limitToPages` - an array of page IDs where this enhancer should be called. This is optional. This property (array)
64 evaluates to only trigger an enhancer for specific pages. In case of special plugin pages it is
65 useful to only enhance pages with IDs, to speed up performance for building page routes of all other pages.
70 The Simple Enhancer works with various route arguments to map them to a argument to be used later-on.
72 `index.php?id=13&category=241&tag=Benni`
74 `https://www.example.com/path-to/my-page/241/Benni`
76 The configuration looks like this::
79 # Unique name for the enhancers, used internally for referencing
83 routePath: '/show-by-category/{category_id}/{tag}'
87 category_id: '[0-9]{1..3}'
88 tag: '^[a-zA-Z0-9].*$'
90 category_id: 'category'
92 The configuration option `routePath` defines the static keyword (previously known to some as "postVarSets" keyword for
93 some TYPO3 folks), and the available placeholders.
95 The `defaults` section defines which URL parameters are optional. If the parameters are omitted on generation, they
96 can receive a default value, and do not need a placeholder - it is also possible to add them at the very end of the
99 The `requirements` section exactly specifies what kind of parameter should be added to that route as regular expression.
100 This way, it is configurable to only allow integer values for e.g. pagination. If the requirements are too loose, a
101 URL signature parameter ("cHash") is added to the end of the URL which cannot be removed.
103 The `_arguments` section defines what Route Parameters should be available to the system. In this example, the
104 placeholder is called `category_id` but the URL generation receives the argument `category`, so this is mapped to
107 An enhancer is only there to replace a set of placeholders and fill in URL parameters or resolve them properly
108 later-on, but not to substitute the values with aliases, this can be achieved by Aspects.
114 The Plugin Enhancer works with plugins on a page that are commonly known as `Pi-Based Plugins`, where previously
115 the following GET/POST variables were used:
117 `index.php?id=13&tx_felogin_pi1[forgot]=1&&tx_felogin_pi1[user]=82&tx_felogin_pi1[hash]=ABCDEFGHIJKLMNOPQRSTUVWXYZ012345`
119 The base for the plugin enhancer is to configure a so-called "namespace", in this case `tx_felogin_pi1` - the plugin's
122 The Plugin Enhancer explicitly sets exactly one additional variant for a specific use-case. In case of Frontend Login,
123 we would need to set up multiple configurations of Plugin Enhancer for forgot and recover passwords.
131 routePath: '/forgot-password/{user}/{hash}'
132 namespace: 'tx_felogin_pi1'
137 hash: '^[a-zA-Z0-9]{32}$'
139 If a URL is generated with the given parameters to link to a page, the result will look like this:
141 `https://www.example.com/path-to/my-page/forgot-password/82/ABCDEFGHIJKLMNOPQRSTUVWXYZ012345`
143 If the input given to generate the URL does not meet the requirements, the route enhancer does not offer the
144 variant and the parameters are added to the URL as regular query parameters. If e.g. the user parameter would be more
145 than three characters, or non-numeric, this enhancer would not match anymore.
147 As you see, the Plugin Enhancer is used to specify placeholders and requirements, with a given namespace.
149 If you want to replace the user ID (in this example "82") with the username, you would need an aspect that can be
150 registered within any enhancer, but see below for details on Aspects.
153 Extbase Plugin Enhancer
154 -----------------------
156 When creating extbase plugins, it is very common to have multiple controller/action combinations. The Extbase Plugin
157 Enhancer is therefore an extension to the regular Plugin Enhancer, except for the functionality that multiple variants
158 are generated, typically built on the amount of controller/action pairs.
160 The `namespace` option is omitted, as this is built with `extension` and `plugin` name.
162 The Extbase Plugin enhancer with the configuration below would now apply to the following URLs:
164 * `index.php?id=13&tx_news_pi1[controller]=News&tx_news_pi1[action]=list`
165 * `index.php?id=13&tx_news_pi1[controller]=News&tx_news_pi1[action]=list&tx_news_pi1[page]=5`
166 * `index.php?id=13&tx_news_pi1[controller]=News&tx_news_pi1[action]=detail&tx_news_pi1[news]=13`
167 * `index.php?id=13&tx_news_pi1[controller]=News&tx_news_pi1[action]=archive&tx_news_pi1[year]=2018&&tx_news_pi1[month]=8`
169 And generate the following URLs
171 * `https://www.example.com/path-to/my-page/list/`
172 * `https://www.example.com/path-to/my-page/list/5`
173 * `https://www.example.com/path-to/my-page/detail/13`
174 * `https://www.example.com/path-to/my-page/archive/2018/8`
185 - { routePath: '/list/{page}', _controller: 'News::list', _arguments: {'page': '@widget_0/currentPage'} }
186 - { routePath: '/tag/{tag_name}', _controller: 'News::list', _arguments: {'tag_name': 'overwriteDemand/tags'}}
187 - { routePath: '/blog/{news_title}', _controller: 'News::detail', _arguments: {'news_title': 'news'} }
188 - { routePath: '/archive/{year}/{month}', _controller: 'News::archive' }
189 defaultController: 'News::list'
195 In this example, you also see that the `_arguments` parameter can be used to bring them into sub properties of an array,
196 which is typically the case within demand objects for filtering functionality.
198 For the Extbase Plugin Enhancer, it is also possible to configure the namespace directly by skipping `extension`
199 and `plugin` properties and just using the `namespace` property as in the regular Plugin Enhancer.
204 Now that we've looked into ways on how to extend a route to a page with arguments, and to put them into the URL
205 path as segments, the detailed logic within one placeholder is in an aspect. The most common practice of an aspect
206 is a so-called mapper. Map `{news_title}` which is a UID within TYPO3 to the actual news title, which is a field
207 within the database table.
209 An aspect can be a way to modify, beautify or map an argument from the URL generation into a placeholder. That's why
210 the terms "Mapper" and "Modifier" will pop up, depending on the different cases.
212 Aspects are registered within one single enhancer configuration with the option `aspects` and can be used with any
215 Let's start with some simpler examples first:
221 The StaticValueMapper replaces values simply on a 1:1 mapping list of an argument into a speaking segment, useful
222 for a checkout process to define the steps into "cart", "shipping", "billing", "overview" and "finish", or in a
223 simpler example to create speaking segments for all available months.
225 The configuration could look like this:
236 - { routePath: '/{year}/{month}', _controller: 'News::archive' }
237 defaultController: 'News::list'
242 type: StaticValueMapper
258 You'll see the placeholder "month" where the aspect replaces the value to a speaking segment.
260 It is possible to add an optional `localeMap` to that aspect to use the locale of a value to use in multi-language
272 - { routePath: '/{year}/{month}', _controller: 'News::archive' }
273 defaultController: 'News::list'
278 type: StaticValueMapper
312 The enhanced part of a route path could be `/archive/{year}/{month}` - however, in multi-language setups, it should be
313 possible to rename `/archive/` depending on the language that is given for this page translation. This modifier is a
314 good example where a route path is modified but is not affected by arguments.
316 The configuration could look like this::
325 - { routePath: '/{localized_archive}/{year}/{month}', _controller: 'News::archive' }
326 defaultController: 'News::list'
332 - locale: 'fr_FR.*|fr_CA.*'
337 You'll see the placeholder "localized_archive" where the aspect replaces the localized archive based on the locale of
338 the language of that page.
344 A static range mapper allows to avoid the `cHash` and narrow down the available possibilities for a placeholder,
345 and to explicitly define a range for a value, which is recommended for all kinds of pagination functionalities.
356 - { routePath: '/list/{page}', _controller: 'News::list', _arguments: {'page': '@widget_0/currentPage'} }
357 defaultController: 'News::list'
364 type: StaticRangeMapper
368 This limits down the pagination to max. 100 pages, if a user calls the news list with page 101, then the route enhancer
369 does not match and would not apply the placeholder.
374 If an extension ships with a slug field, or a different field used for the speaking URL path, this database field
375 can be used to build the URL::
384 - { routePath: '/detail/{news_title}', _controller: 'News::detail', _arguments: {'news_title': 'news'} }
385 defaultController: 'News::detail'
388 type: PersistedAliasMapper
389 tableName: 'tx_news_domain_model_news'
390 routeFieldName: 'path_segment'
391 routeValuePrefix: '/'
393 The PersistedAliasMapper looks up (via a so-called delegate pattern under the hood) to map the given value to a
394 a URL. The property `tableName` points to the database table, property `routeFieldName` is the field which will be
395 used within the route path for example.
397 The special `routeValuePrefix` is used for TCA type `slug` fields where the prefix `/` is within all fields of the
398 field names, which should be removed in the case above.
400 If a field is used for `routeFieldName` that is not prepared to be put into the route path, e.g. the news title field,
401 it still must be ensured that this is unique. On top, if there are special characters like spaces they will be
402 URL-encoded, to ensure a definitive value, a slug TCA field is recommended.
404 PersistedPatternMapper
405 ----------------------
407 When a placeholder should be fetched from multiple fields of the database, the PersistedPatternMapper is for you.
408 It allows to combine various fields into one variable, ensuring a unique value by e.g. adding the UID to the field
409 without having the need of adding a custom slug field to the system.
417 extension: BlogExample
420 - { routePath: '/blog/{blogpost}', _controller: 'Blog::detail', _arguments: {'blogpost': 'post'} }
421 defaultController: 'Blog::detail'
424 type: PersistedPatternMapper
425 tableName: 'tx_blogexample_domain_model_post'
426 routeFieldPattern: '^(?P<title>.+)-(?P<uid>\d+)$'
427 routeFieldResult: '{title}-{uid}'
429 The `routeFieldPattern` option builds the title and uid fields from the database, the `routeFieldResult` shows
430 how the placeholder will be output.
435 Some notes to the implementation:
437 While accessing a page in TYPO3 in the Frontend, all arguments are currently built back into the global
438 GET parameters, but are also available as so-called `PageArguments` object, which is then used to be signed and verified
439 that they are valid, when handing them to process a frontend request further.
441 If there are dynamic parameters (= parameters which are not strictly limited), a verification GET parameter `cHash`
442 is added, which can and should not be removed from the URL. The concept of manually activating or deactivating
443 the generation of a `cHash` is not optional anymore, but strictly built-in to ensure proper URL handling. If you
444 really have the requirement to never have a cHash argument, ensure that all placeholders are having strict definitions
445 on what could be the result of the page segment (e.g. pagination), and feel free to build custom mappers.
447 Setting the TypoScript option `typolink.useCacheHash` is not necessary anymore when running with a site configuration.
449 Please note that Enhancers and Page-based routing is only available for pages that are built with a site configuration.
451 All existing APIs like `typolink` or functionality evaluate the new Page Routing API directly and come with route
454 Please note that if you update the Site configuration with enhancers that you need to clear all caches.
456 .. index:: Frontend, PHP-API