---
title: "Route enhancers"
manual: "Academic Profiles"
version: "main"
source: "Configuration/RouteEnhancers/Index.rst"
rendered: "2026-10-02T20:47:49+00:00"
---

# Route enhancers {#configuration-route-enhancers}

This extension ships three ready made route enhancers below
`Configuration/Routes/`. TYPO3 does not read those files on its own —
they are fragments that have to be imported from the configuration of the site
which shows the plugins.

Each file covers exactly one plugin, so which of them you import follows from
which plugins the site actually uses.

## What the files enhance {#what-the-files-enhance}

-   **`Detail.yaml`**

    Enhancer `ProfileDetailPlugin` for the plugin `Detail`,
    argument namespace `tx_academicpersons_detail`. One route,
    `/{profile_name}`, for the `detail` action, mapping the argument
    `profile`. The path segment is resolved by a
    `PersistedAliasMapper` on the table
    `tx_academicpersons_domain_model_profile` over the field
    `slug`.

-   **`List.yaml`**

    Enhancer `ProfileListPlugin` for the plugin `List`, argument
    namespace `tx_academicpersons_list`. Two routes for the `list`
    action: `{localized_page}-{page}` for `demand/currentPage` and
    `/{letter}` for `demand/alphabetFilter`. The page number is
    limited to a `StaticRangeMapper` from 1 to 1000, the letter to a
    `StaticRangeMapper` from `a` to `z`, and the word in front of
    the page number is translated by a `LocaleModifier` — `page` by
    default, `seite` for German.

    Three more routes carry a [view mode](../Index.html#configuration-view-modes)
    other than the default, for `demand/viewMode`: the static segment
    `view-mode` followed by `{viewMode}` alone, followed by the page
    route, and followed by the letter route - there is no route with a page
    and a letter, because a list under a letter is not paginated. The mode is
    mapped by a `StaticValueMapper` holding `list` and `table`, and
    every variable of the enhancer carries explicit `requirements`.

    Eighteen more routes carry the [visitor filters](#configuration-route-enhancers-filters): the function type, the
    organisational unit or both, each alone, with the page route and with the
    letter route, and each of these with the view mode segment in front of the
    page or the letter.

-   **`ListAndDetail.yaml`**

    Enhancer `ProfileListAndDetailPlugin` for the plugin
    `ListAndDetail`, argument namespace
    `tx_academicpersons_listanddetail`. It is the union of the two above:
    the detail route, the pagination route, the letter route, the three
    view mode routes and the eighteen filter routes, with the same aspects,
    because that plugin renders both the list and the detail view.

## Which file to import {#which-file-to-import}

Which of them you import follows from which plugins the site actually uses:

-   A site that puts the **List** plugin on one page and the
    **Detail** plugin on another — the usual setup, where the list
    links to the detail page through the plugin setting
    `detailPid` — imports `List.yaml` **and**
    `Detail.yaml`.
-   A site that puts the single **ListAndDetail** plugin on one page
    imports `ListAndDetail.yaml` only.
-   A site that uses both variants imports all three, and then has to bound
    each of them to its own pages — see the next section.

The remaining plugins of this extension — `SelectedProfiles`,
`SelectedContracts` and `Card` — take no frontend arguments, so no
enhancer is shipped for them.

## Limiting an enhancer to its pages {#limiting-an-enhancer-to-its-pages}

An enhancer is offered to **every** page of the site unless it says otherwise,
and TYPO3 takes the first candidate route whose path matches *and* whose
aspects resolve. Two enhancers are not kept apart by belonging to different
plugins, nor by carrying different keys — neither is what the matcher looks at.

The three files of this extension describe the same two views, so their routes
overlap by construction:

| Route | Declared in |
| --- | --- |
| `/{profile_name}` | `Detail.yaml` and `ListAndDetail.yaml` |
| `{localized_page}-{page}` | `List.yaml` and `ListAndDetail.yaml` |
| `/{letter}` | `List.yaml` and `ListAndDetail.yaml` |
| the three `/view-mode/{viewMode}` routes | `List.yaml` and `ListAndDetail.yaml` |
| the eighteen filter routes | `List.yaml` and `ListAndDetail.yaml` |

Each pair is identical down to the mapper, so importing more than one file
without saying where it applies means the file imported first takes those URLs
on every page of the site. The plugin on the other page then never receives its
argument: the dedicated detail page answers `404`, and the combined
plugin renders the unfiltered list where a letter was asked for.

Only resolving is ambiguous. Generating a URL is scoped to the plugin namespace
being linked, so the links keep looking right, which is why this surfaces as a
broken page rather than as a broken link.

`limitToPages` is the answer, and with it in place the import order no
longer matters:

**config/sites/my_site/config.yaml**

```yaml
imports:
  - resource: 'EXT:academic_persons/Configuration/Routes/List.yaml'
  - resource: 'EXT:academic_persons/Configuration/Routes/ListAndDetail.yaml'
  - resource: 'EXT:academic_persons/Configuration/Routes/Detail.yaml'

routeEnhancers:
  ProfileListPlugin:
    limitToPages: [12, 13]
  ProfileListAndDetailPlugin:
    limitToPages: [14]
  ProfileDetailPlugin:
    limitToPages: [15]
```

The uids are those of the pages carrying the plugin in question, and they are
the uids of the **default language**: matching derives the page as
`l10n_parent ?: uid`, so one list covers every translation of that page.
Plain page uids work on every TYPO3 version this extension supports.

A site that imports a single file needs no limitation for this extension, but
adding it is still worth the two lines. What keeps a route path of the same
shape from another extension apart is only that its mapper rejects the value —
a slug that happens to exist in both tables is enough to make the two compete.

## What the URLs look like {#what-the-urls-look-like}

Assuming the list plugin sits on a page with the slug `/persons` and the
detail plugin on `/persons/profile`, the URLs change as follows.

**Without the enhancers**

```text
/persons?tx_academicpersons_list%5Bdemand%5D%5BcurrentPage%5D=2
/persons?tx_academicpersons_list%5Bdemand%5D%5BalphabetFilter%5D=m
/persons/profile?tx_academicpersons_detail%5Bprofile%5D=42
```

**With the enhancers imported**

```text
/persons/page-2
/persons/m
/persons/profile/jane-doe
```

A list shown as a table, when its default mode is the tiles:

**View mode routes**

```text
/persons/view-mode/table
/persons/view-mode/table/page-2
/persons/view-mode/table/m
```

The link to the default mode carries no mode, so the list in its default mode
keeps the URLs above. A segment the map does not hold, such as
`/persons/view-mode/slider`, matches no route, and the site answers with
its page-not-found response. A segment the map holds always resolves, and the
list decides what it renders: `/persons/view-mode/list` on a list whose
default is the tiles, or `/persons/view-mode/table` on one without the
switch, shows the default mode. No link of the list leads there.

## A view mode of your own in the URL {#configuration-route-enhancers-view-modes}

A mode the map of the `StaticValueMapper` does not hold cannot be a
segment. Its links keep the mode as a query argument with a cHash, and work
as they are. To give it a segment, add it to the map of the shipped enhancer in
the configuration of the site, next to the import - a site configuration merges
its own `routeEnhancers` over the imported ones:

**config/sites/my_site/config.yaml**

```yaml
imports:
  - resource: 'EXT:academic_persons/Configuration/Routes/List.yaml'

routeEnhancers:
  ProfileListPlugin:
    aspects:
      viewMode:
        map:
          contact: contact
```

The key of the map is the segment - any text without a slash - and the value the
mode, so the segment may differ from the name of the mode. The shipped modes stay
in the map.

## Filtered lists {#configuration-route-enhancers-filters}

A list element that offers a [visitor filter](../Index.html#configuration-visitor-filters) renders a
form above the profiles. It posts to the `filter` action of the plugin,
which answers with a redirect to the URL of the filtered list. The form needs no
JavaScript, and the filtered list is cached like any other. With the enhancers
imported that URL is speaking:

**Filter routes**

```text
/persons/function/professor
/persons/unit/biology
/persons/function/professor/unit/biology
/persons/function/professor/page-2
/persons/function/professor/b
/persons/function/professor/view-mode/table
/persons/function/professor/view-mode/table/page-2
```

The words `function` and `unit` are translated by a `LocaleModifier`
like the word in front of the page number, `funktion` and `einheit` for
German. The segment after them is the `slug` of the function type or the
organisational unit, generated from its name when it is saved, and in a
translated list the slug of its translation. A slug is unique per language in
the whole installation, so a list resolves the records of a folder in another
site as well.

A record without a slug, or with a slug an editor gave a slash, cannot be a
segment. Its filter stays a query argument with a cHash, and works as it is. The upgrade wizard
`academicPersons_fillFilterSlugs` gives every such record its slug, see
[5\. Generate the URL segments of the filter records](../../Upgrade/Index.html#upgrade-step-filter-slugs). A slug no record carries matches no route,
and the site answers with its page-not-found response.

## Extend the shipped enhancer, never add a second one {#configuration-route-enhancers-extend}

A change to the routes of a list belongs into the shipped enhancer key, written
in the site configuration next to the import: a different word for a key, a
locale of your own, a view mode, or a `limitToPages`. The site
configuration merges its own `routeEnhancers` over the imported ones.

**config/sites/my_site/config.yaml**

```yaml
imports:
  - resource: 'EXT:academic_persons/Configuration/Routes/List.yaml'

routeEnhancers:
  ProfileListPlugin:
    aspects:
      function_key:
        default: 'role'
```

A second enhancer for the same plugin, under a key of your own, does not replace
the shipped one. Both are offered to the same page, the first route that matches
and resolves wins, and which one that is depends on the import order. Links
generated by one enhancer then resolve through the other, or not at all.

## Caveats {#caveats}

-   The detail route needs a slug. `PersistedAliasMapper` resolves the
    path segment against the `slug` field of the profile record, so a
    profile whose slug is empty cannot be reached through the enhanced URL. The
    slug is generated by the TCA `slug` field, which means it is filled
    when the record is saved in the backend. Profiles created by
    `academicpersons:createprofiles` are persisted through the Extbase
    persistence manager and therefore never pass the `DataHandler`, so
    those records — and records that predate the field — start out with an
    empty slug and have to be saved once in the backend before the enhanced
    URL resolves.
-   The two list routes are alternatives, not a combination. A link that
    carries a page number *and* a letter matches the pagination route, and the
    letter stays behind as a query argument —
    `/persons/page-2?tx_academicpersons_list[demand][alphabetFilter]=m`.
-   Only the mapped value ranges are put into the path. A page number above
    1000, and the empty filter value that the **A-Z** reset link of the
    alphabet pagination submits, are outside the mapped ranges, so those links
    keep their query argument.
-   The `localeMap` of the `LocaleModifier` is matched against the
    locale of the site language, with the underscores replaced by hyphens and
    anchored at the start. The shipped map lists `en_EN.*` and
    `de_DE.*`, which means a German language configured as `de-DE`
    is translated to `seite` while a plain `de` is not. The same holds
    for `funktion` and `einheit` of the filter routes. Adjust the maps to
    the locales your site actually uses.
-   Unlike the program list of **academic_programs**, the pagination
    and the alphabet filter of this extension are rendered as links, not as a
    form, so their own requests do carry the arguments in the URL and are
    enhanced.
