---
title: "Route enhancers"
manual: "Academic Programs"
version: "main"
source: "Configuration/RouteEnhancers/Index.rst"
rendered: "2026-09-27T09:40:45+00:00"
---

# Route enhancers {#configuration-route-enhancers}

This extension ships one ready made route enhancer in
`Configuration/Yaml/Routes.yaml`. TYPO3 does not read that file on its
own — it is a fragment that has to be imported from the configuration of the
site which shows the plugin.

## What the file enhances {#what-the-file-enhances}

The file declares a single enhancer of type `Extbase` named
`AcademicPrograms`, bound to the plugin `ProgramList` of the
extension `AcademicPrograms`. That pair is what determines the argument
namespace the enhancer works on — `tx_academicprograms_programlist`.

It registers one route for the `list` action of
`\FGTCLB\AcademicPrograms\Controller\ProgramController`:

**EXT:academic_programs/Configuration/Yaml/Routes.yaml**

```yaml
routes:
  - routePath: '/{sorting_field}/{sorting_direction}'
    _controller: 'Program::list'
    _arguments:
      sorting_field: demand/sortingField
      sorting_direction: demand/sortingDirection
```

Both path variables are mapped by a `StaticValueMapper`, so only the
values below ever appear in a URL, and anything else is rejected:

-   `sorting_field` — the segment `title` selects the demand value
    `title`, `last-updated` selects `lastUpdated` and
    `sorting` selects `sorting`.
-   `sorting_direction` — `asc` and `desc`.

Those are the values of
`\FGTCLB\AcademicPrograms\Enumeration\SortingOptions`, which is also what
the sorting select field of the plugin offers.

Neither variable carries a default, on purpose: a link that uses the default
sorting generates `/title/asc`, not the plain page URL. The plain page URL
is where the content element's preset categories and sorting apply, and the
list redirects every form submission to a URL with the sorting in it so that a
visitor who cleared a preset category does not get it back. A default would
turn that redirect into the plain page URL again. The same holds for an
enhancer a site writes for this plugin itself. A path with the field alone,
such as `/last-updated`, does not resolve, and a link to the list that
carries no sorting at all - the action URL of the plugin's own form, for
example - does not enter the route and keeps its plugin arguments in the query
string.

## Importing it into a site configuration {#importing-it-into-a-site-configuration}

Add the resource to the `imports` of the site that contains the page with
the program list plugin:

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

```yaml
imports:
  - resource: 'EXT:academic_programs/Configuration/Yaml/Routes.yaml'
```

The import is merged into the site configuration, so an installation that
already defines other enhancers keeps them as long as no key is named
`AcademicPrograms` twice. That is a statement about the merge and about
nothing else: distinct keys keep the entries, they do not keep two enhancers
from producing routes which match the same URL.

## Limiting the enhancer to its page {#limiting-the-enhancer-to-its-page}

TYPO3 offers every enhancer declared in a site configuration to **every** page
of that site unless the enhancer says otherwise, and it takes the first
candidate route whose path matches *and* whose aspects resolve. The order the
candidates are tried in is the order of the `imports`.

The route of this extension is comparatively hard to collide with, and that is
owed to its mappers rather than to its key: both path variables are handled by
a `StaticValueMapper`, so a candidate is only accepted when the segments
are one of the three sorting fields followed by `asc` or `desc`.
Anything else is rejected and the next enhancer gets its turn. A route variable
that is mapped less narrowly — one whose aspect comes without an explicit
`requirements` entry compiles to `.+` and crosses slashes — has no
such protection, and even here a second extension mapping values of the same
spelling is enough to make the two compete.

`limitToPages` settles it by naming the pages the enhancer applies to:

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

```yaml
imports:
  - resource: 'EXT:academic_programs/Configuration/Yaml/Routes.yaml'

routeEnhancers:
  AcademicPrograms:
    limitToPages: [23]
```

The uid is the one of the page carrying the list plugin, and it is the uid of
the **default language**: matching derives the page as `l10n_parent ?: uid`,
so a single entry covers every translation of that page. Plain page uids work
on every TYPO3 version this extension supports.

In **academic_persons** the same mechanism is not a precaution but a
requirement — that extension ships three enhancers whose routes overlap each
other by construction. See [its route enhancer documentation](https://docs.typo3.org/p/fgtclb/academic-persons/main/en-us/Configuration/RouteEnhancers/Index.html).

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

Assuming the plugin sits on a page with the slug `/programs`, a link that
sorts by the last update, descending, is built without the enhancer as (line
breaks added, and the brackets not encoded, for reading):

```text
/programs?tx_academicprograms_programlist[action]=list
    &tx_academicprograms_programlist[controller]=Program
    &tx_academicprograms_programlist[demand][sortingDirection]=desc
    &tx_academicprograms_programlist[demand][sortingField]=lastUpdated
    &cHash=…
```

and with the enhancer imported as:

```text
/programs/last-updated/desc
```

The sorting and filter form of the plugin submits by POST, and the plugin
answers with a redirect to such a URL, so the address bar shows the enhanced
path after a submit. A category filter is not part of the route and stays in the
query string; it needs no cache hash, because the demand is excluded from it
([Important: The list demand is not part of the cache hash](../../Changelog/3.0/Important-ListDemandIsNotPartOfTheCacheHash.html#important-1790226107)):

```text
/programs/title/asc?tx_academicprograms_programlist[demand][filterCollection][categories]=2
```

## Caveats {#caveats}

Two points are worth knowing:

-   The enhancer covers the list plugin only. The detail plugin
    (`ProgramDetails`) takes no arguments — it renders the program of the
    page it sits on — so there is nothing to map into a path for it.
-   The two mappers are independent, so every one of the six combinations they
    can spell is reachable — and `SortingOptions` defines exactly those
    six, so each path the enhancer resolves is an ordering the plugin really
    renders. Until the reversed page sorting was added, `/sorting/desc`
    resolved to an option that did not exist and was silently dropped.
