[!!!][TASK] Remove deprecated code from TableController
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Documentation / Changelog / 9.5 / Feature-86365-RoutingEnhancersAndAspects.rst
1 .. include:: ../../Includes.txt
2 .. highlight:: yaml
3
4 ===============================================
5 Feature: #86365 - Routing Enhancers and Aspects
6 ===============================================
7
8 See :issue:`86365`
9
10 Description
11 ===========
12
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.
15
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".
18
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.
21
22 To give you an overview of what the distinction is, we take a regular page which is available under
23
24 `https://www.example.com/path-to/my-page`
25
26 to access the Page with ID 13.
27
28 Enhancers are ways to extend this route with placeholders on top of this specific route to a page.
29
30 `https://www.example.com/path-to/my-page/products/{product-name}`
31
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.
35
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.
39
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.
42
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
45 resolving.
46
47 Enhancers
48 ^^^^^^^^^
49
50 TYPO3 comes with the following enhancers out of the box:
51
52 - Simple Enhancer (enhancer type "Simple")
53 - Plugin Enhancer (enhancer type "Plugin")
54 - Extbase Plugin Enhancer (enhancer type "Extbase")
55
56 Custom enhancers can be registered by adding an entry to an extensions :file:`ext_localconf.php`.
57
58 :php:`$GLOBALS['TYPO3_CONF_VARS']['SYS']['routing']['CustomPlugin'] = \MyVendor\MyPackage\Routing\CustomEnhancer::class;`
59
60 Within a configuration, an enhancer always evaluates the following properties:
61
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.
66
67 Simple Enhancer
68 ---------------
69
70 The Simple Enhancer works with various route arguments to map them to a argument to be used later-on.
71
72 `index.php?id=13&category=241&tag=Benni`
73 results in
74 `https://www.example.com/path-to/my-page/241/Benni`
75
76 The configuration looks like this::
77
78 routeEnhancers:
79 # Unique name for the enhancers, used internally for referencing
80 CategoryListing:
81 type: Simple
82 limitToPages: [13]
83 routePath: '/show-by-category/{category_id}/{tag}'
84 defaults:
85 tag: ''
86 requirements:
87 category_id: '[0-9]{1..3}'
88 tag: '^[a-zA-Z0-9].*$'
89 _arguments:
90 category_id: 'category'
91
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.
94
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
97 `routePath`.
98
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.
102
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
105 this very name.
106
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.
109
110
111 Plugin Enhancer
112 ---------------
113
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:
116
117 `index.php?id=13&tx_felogin_pi1[forgot]=1&&tx_felogin_pi1[user]=82&tx_felogin_pi1[hash]=ABCDEFGHIJKLMNOPQRSTUVWXYZ012345`
118
119 The base for the plugin enhancer is to configure a so-called "namespace", in this case `tx_felogin_pi1` - the plugin's
120 namespace.
121
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.
124
125 ::
126
127 routeEnhancers:
128 ForgotPassword:
129 type: Plugin
130 limitToPages: [13]
131 routePath: '/forgot-password/{user}/{hash}'
132 namespace: 'tx_felogin_pi1'
133 defaults:
134 forgot: "1"
135 requirements:
136 user: '[0-9]{1..3}'
137 hash: '^[a-zA-Z0-9]{32}$'
138
139 If a URL is generated with the given parameters to link to a page, the result will look like this:
140
141 `https://www.example.com/path-to/my-page/forgot-password/82/ABCDEFGHIJKLMNOPQRSTUVWXYZ012345`
142
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.
146
147 As you see, the Plugin Enhancer is used to specify placeholders and requirements, with a given namespace.
148
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.
151
152
153 Extbase Plugin Enhancer
154 -----------------------
155
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.
159
160 The `namespace` option is omitted, as this is built with `extension` and `plugin` name.
161
162 The Extbase Plugin enhancer with the configuration below would now apply to the following URLs:
163
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`
168
169 And generate the following URLs
170
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`
175
176 ::
177
178 routeEnhancers:
179 NewsPlugin:
180 type: Extbase
181 limitToPages: [13]
182 extension: News
183 plugin: Pi1
184 routes:
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'
190 defaults:
191 page: '0'
192 requirements:
193 page: '\d+'
194
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.
197
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.
200
201 Aspects
202 ^^^^^^^
203
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.
208
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.
211
212 Aspects are registered within one single enhancer configuration with the option `aspects` and can be used with any
213 enhancer.
214
215 Let's start with some simpler examples first:
216
217
218 StaticValueMapper
219 -----------------
220
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.
224
225 The configuration could look like this:
226
227 ::
228
229 routeEnhancers:
230 NewsArchive:
231 type: Extbase
232 limitToPages: [13]
233 extension: News
234 plugin: Pi1
235 routes:
236 - { routePath: '/{year}/{month}', _controller: 'News::archive' }
237 defaultController: 'News::list'
238 defaults:
239 month: ''
240 aspects:
241 month:
242 type: StaticValueMapper
243 map:
244 january: 1
245 february: 2
246 march: 3
247 april: 4
248 may: 5
249 june: 6
250 july: 7
251 august: 8
252 september: 9
253 october: 10
254 november: 11
255 december: 12
256
257
258 You'll see the placeholder "month" where the aspect replaces the value to a speaking segment.
259
260 It is possible to add an optional `localeMap` to that aspect to use the locale of a value to use in multi-language
261 setups.
262
263 ::
264
265 routeEnhancers:
266 NewsArchive:
267 type: Extbase
268 limitToPages: [13]
269 extension: News
270 plugin: Pi1
271 routes:
272 - { routePath: '/{year}/{month}', _controller: 'News::archive' }
273 defaultController: 'News::list'
274 defaults:
275 month: ''
276 aspects:
277 month:
278 type: StaticValueMapper
279 map:
280 january: 1
281 february: 2
282 march: 3
283 april: 4
284 may: 5
285 june: 6
286 july: 7
287 august: 8
288 september: 9
289 october: 10
290 november: 11
291 december: 12
292 localeMap:
293 - locale: 'de_.*'
294 map:
295 januar: 1
296 februar: 2
297 maerz: 3
298 april: 4
299 mai: 5
300 juni: 6
301 juli: 7
302 august: 8
303 september: 9
304 oktober: 10
305 november: 11
306 dezember: 12
307
308
309 LocaleModifier
310 --------------
311
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.
315
316 The configuration could look like this::
317
318 routeEnhancers:
319 NewsArchive:
320 type: Extbase
321 limitToPages: [13]
322 extension: News
323 plugin: Pi1
324 routes:
325 - { routePath: '/{localized_archive}/{year}/{month}', _controller: 'News::archive' }
326 defaultController: 'News::list'
327 aspects:
328 localized_archive:
329 type: LocaleModifier
330 default: 'archive'
331 localeMap:
332 - locale: 'fr_FR.*|fr_CA.*'
333 value: 'archives'
334 - locale: 'de_DE.*'
335 value: 'archiv'
336
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.
339
340
341 StaticRangeMapper
342 -----------------
343
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.
346
347 ::
348
349 routeEnhancers:
350 NewsPlugin:
351 type: Extbase
352 limitToPages: [13]
353 extension: News
354 plugin: Pi1
355 routes:
356 - { routePath: '/list/{page}', _controller: 'News::list', _arguments: {'page': '@widget_0/currentPage'} }
357 defaultController: 'News::list'
358 defaults:
359 page: '0'
360 requirements:
361 page: '\d+'
362 aspects:
363 page:
364 type: StaticRangeMapper
365 start: '1'
366 end: '100'
367
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.
370
371 PersistedAliasMapper
372 --------------------
373
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::
376
377 routeEnhancers:
378 NewsPlugin:
379 type: Extbase
380 limitToPages: [13]
381 extension: News
382 plugin: Pi1
383 routes:
384 - { routePath: '/detail/{news_title}', _controller: 'News::detail', _arguments: {'news_title': 'news'} }
385 defaultController: 'News::detail'
386 aspects:
387 news_title:
388 type: PersistedAliasMapper
389 tableName: 'tx_news_domain_model_news'
390 routeFieldName: 'path_segment'
391 routeValuePrefix: '/'
392
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.
396
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.
399
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.
403
404 PersistedPatternMapper
405 ----------------------
406
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.
410
411 ::
412
413 routeEnhancers:
414 Blog:
415 type: Extbase
416 limitToPages: [13]
417 extension: BlogExample
418 plugin: Pi1
419 routes:
420 - { routePath: '/blog/{blogpost}', _controller: 'Blog::detail', _arguments: {'blogpost': 'post'} }
421 defaultController: 'Blog::detail'
422 aspects:
423 blogpost:
424 type: PersistedPatternMapper
425 tableName: 'tx_blogexample_domain_model_post'
426 routeFieldPattern: '^(?P<title>.+)-(?P<uid>\d+)$'
427 routeFieldResult: '{title}-{uid}'
428
429 The `routeFieldPattern` option builds the title and uid fields from the database, the `routeFieldResult` shows
430 how the placeholder will be output.
431
432 Impact
433 ======
434
435 Some notes to the implementation:
436
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.
440
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.
446
447 Setting the TypoScript option `typolink.useCacheHash` is not necessary anymore when running with a site configuration.
448
449 Please note that Enhancers and Page-based routing is only available for pages that are built with a site configuration.
450
451 All existing APIs like `typolink` or functionality evaluate the new Page Routing API directly and come with route
452 enhancers.
453
454 Please note that if you update the Site configuration with enhancers that you need to clear all caches.
455
456 .. index:: Frontend, PHP-API