Slugs / URL parts
The main purpose of this type is to define parts of a URL path to generate and resolve URLs. The according database field is generated automatically.
Table of contents:
Introduction
The main purpose of this type is to define parts of a URL path to generate and resolve URLs.
With a URL like https:// a URL slug is typically a part like
/community or /community/.
Within TYPO3, a slug is always part of the URL "path" - it does not contain scheme, host, HTTP verb, etc.
A slug is usually added to a TCA-based database table, for example pages. The TCA then contains rules for evaluation and definition.
When applied to pages a hierarchy of pages is often represented by a combined slug: From each page the characteristic last part of its slug is used as path segment. The segments are combined into one slug, separated by slashes. The resulting URL's path is therefore similar to the file paths in common operating systems.
However, slugs in TYPO3 are not limited to be separated by slashes. It is possible to use other separators. Furthermore, a single path segment may contain any sign allowed in URLs, including slashes.
It is not required to build hierarchical paths. It is possible to assign a single, unique path segment to each page in a deep page hierarchy. In TYPO3 the only requirement is that the slug for each page in a domain must be unique.
If a TCA table contains a field called "slug", it needs to be filled for every existing record. It can be shown and edited via regular backend forms, and is also evaluated during persistence via DataHandler.
The default behaviour of a slug is as follows:
- A slug only contains characters which are allowed within URLs. Spaces, commas and other special characters are converted to a fallback character.
- A slug is always lower-cased.
- A slug is unicode-aware.
It is possible to build a default value from the rootline (very helpful for pages, or categorized slugs), but also to just generate a "speaking" segment from e.g. a news title.
Sanitation and Validation configuration options apply when persisting a record via DataHandler.
In the backend forms a validation happens by an AJAX call, which immediately checks any input and receives a new proposal in case the slug is already used.
Example: A basic slug field
The slug of a talk is generated from its type and its title:
The slug of a workshop
<?php
use MyVendor\MyExtension\Slug\TalkSlugPrefix;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [
'slug' => [
'label' => 'my_extension.db:talk.slug',
'config' => [
'type' => 'slug',
'generatorOptions' => [
// For example "workshop/write-your-first-form-element"
'fields' => ['talk_type', 'title'],
],
'appearance' => [
'prefix' => TalkSlugPrefix::class . '->getPrefix',
],
'eval' => 'uniqueInPid',
// The slug repeats the title, which the search finds already
'searchable' => false,
],
],
]);
ExtensionManagementUtility::addToAllTCAtypes(
'tx_myextension_talk',
'slug',
'',
'after:title',
);
Example: A slug field with prefix hook
This example uses a custom slug prefix hook via
config to show the path of the program in
front of the slug of a talk:
The slug of a workshop with the path of the program in front
<?php
use MyVendor\MyExtension\Slug\TalkSlugPrefix;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [
'slug' => [
'label' => 'my_extension.db:talk.slug',
'config' => [
'type' => 'slug',
'generatorOptions' => [
// For example "workshop/write-your-first-form-element"
'fields' => ['talk_type', 'title'],
],
'appearance' => [
'prefix' => TalkSlugPrefix::class . '->getPrefix',
],
'eval' => 'uniqueInPid',
// The slug repeats the title, which the search finds already
'searchable' => false,
],
],
]);
ExtensionManagementUtility::addToAllTCAtypes(
'tx_myextension_talk',
'slug',
'',
'after:title',
);
Properties of the TCA column type slug
| Name | Type | Scope |
|---|---|---|
| array | Display | |
| userFunction | Display | |
| string (list of keywords) | Display / Proc. | |
| string | Display / Proc. | |
| array | Display / Proc. | |
| array | Display / Proc. | |
| string | Display / Proc. | |
| boolean | Display / Proc. | |
| array | Display / Proc. | |
| array | Display / Proc. | |
| array | Display / Proc. | |
| boolean | Display / Proc. | |
| boolean | Display |
-
- Type
- array
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['appearance']
- Scope
- Display
Properties that only apply to how the field is displayed in the backend.
-
- Type
- userFunction
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['appearance']['prefix']
- Scope
- Display
New in version 14.2
The user function now receives the full TCA field configuration (
config) and the field name (field) as additional parameters.Name Provides a string that is displayed in front of the input field. The URL of the site is set by default if nothing has been defined.
Assign a user function. It receives two arguments:
-
The first argument is the parameters array containing
site- The current site objectlanguage- The current language ID (int)Id table- The table name (string)row- The current record data (array)field- The name of the slug field (string)Name config- The full TCA configuration array of the slug field
- The second argument is the reference object
Tca.Slug
The user function should return the string which is then used for display purposes.
The slug of a workshop with the path of the program in front
<?php
use MyVendor\MyExtension\Slug\TalkSlugPrefix;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [
'slug' => [
'label' => 'my_extension.db:talk.slug',
'config' => [
'type' => 'slug',
'generatorOptions' => [
// For example "workshop/write-your-first-form-element"
'fields' => ['talk_type', 'title'],
],
'appearance' => [
'prefix' => TalkSlugPrefix::class . '->getPrefix',
],
'eval' => 'uniqueInPid',
// The slug repeats the title, which the search finds already
'searchable' => false,
],
],
]);
ExtensionManagementUtility::addToAllTCAtypes(
'tx_myextension_talk',
'slug',
'',
'after:title',
);
The user function can be implemented like this:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Slug;
use TYPO3\CMS\Backend\Form\FormDataProvider\TcaSlug;
use TYPO3\CMS\Core\Site\Entity\Site;
/**
* Shows the path of the program in front of the slug of a talk
*/
final class TalkSlugPrefix
{
/**
* @param array{site: Site} $parameters
*/
public function getPrefix(array $parameters, TcaSlug $reference): string
{
return rtrim((string)$parameters['site']->getBase(), '/') . '/program/';
}
}
-
- Type
- string (list of keywords)
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['eval']
- Scope
- Display / Proc.
Configuration of field evaluation.
Keywords:
- unique
- Evaluate if a record is unique in the whole TYPO3 installation (specific to a language). This option is recommended as it allows to show any record stored inside other sites. The only downside is that it is not possible to have the same slug on multiple sites.
- uniqueInSite
-
Requires the field to be unique for the current site and language. This allows for multiple records of the same table to have the same slug as long as these records are separated by their sites. Consequently records of a foreign site are not accessible with
uniquesince slugs are looked up respecting the current site.Insite Warning
Be aware that using this option makes it impossible to show records stored inside other sites. If this is required,
uniqueshould be used instead. - uniqueInPid
- Requires the field to be unique for the current PID among other records on the same page.
No other eval setting is checked for. It is possible to set different eval options, however it is recommended not to do so.
The slug of a conference
<?php
use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
'slug' => [
'label' => 'my_extension.db:conference.slug',
'config' => [
'type' => 'slug',
'generatorOptions' => [
// The short title, or the title if there is no short title
'fields' => [['short_title', 'title']],
'replacements' => [
'&' => 'and',
],
'regexReplacements' => [
// A year at the end of the title, the modifier adds it in front
'/\s+\d{4}$/' => '',
],
'postModifiers' => [
ConferenceSlugModifier::class . '->prependYear',
],
],
'fallbackCharacter' => '-',
'eval' => 'uniqueInSite',
],
],
]);
ExtensionManagementUtility::addToAllTCAtypes(
'tx_myextension_conference',
'slug',
'',
'after:title',
);
-
- Type
- string
- Default
- -
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['fallbackCharacter']
- Scope
- Display / Proc.
Character that represents the separator of slug sections, that contain the fieldSeparator.
<?php
use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
'slug' => [
'label' => 'my_extension.db:conference.slug',
'config' => [
'type' => 'slug',
'generatorOptions' => [
// The short title, or the title if there is no short title
'fields' => [['short_title', 'title']],
'replacements' => [
'&' => 'and',
],
'regexReplacements' => [
// A year at the end of the title, the modifier adds it in front
'/\s+\d{4}$/' => '',
],
'postModifiers' => [
ConferenceSlugModifier::class . '->prependYear',
],
],
'fallbackCharacter' => '-',
'eval' => 'uniqueInSite',
],
],
]);
ExtensionManagementUtility::addToAllTCAtypes(
'tx_myextension_conference',
'slug',
'',
'after:title',
);
-
- Type
- array
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']
- Scope
- Display / Proc.
Holds information about the record fields to be used for slug generation:
-
- Type
- array
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['fields']
- Scope
- Display / Proc.
Insert several field names (of type string) that will be considered during slug construction.
Can also be used as nested array to combine multiple fields
[.['nav_ title', 'title'], 'other_ field'] Info
Inserting multiple fields in a simple array would result in an concatenated slug.
Nested array values would result in "take
nav_if not empty, otherwise take value fromtitle title".Examples:
Configuration value Values of an example page record Resulting slug [['nav_ title', 'title']] ['title' => 'Products', 'nav_title' => ''] /products[['title', 'subtitle']] ['title' => 'Products', 'subtitle' => 'Product subtitle']/products['title', 'subtitle']or[['title'], ['subtitle']] ['title' => 'Products', 'subtitle' => 'Product subtitle']/products/product- subtitle ['nav_title', 'title'], 'subtitle' ['title' => 'Products', 'nav_title' => 'Best products', 'subtitle' => 'Product subtitle'] /best-products/ product- subtitle ['seo_title', 'title'], ['nav_ title', 'subtitle'] ['title' => 'Products', 'nav_title' => 'Best products', 'subtitle' => 'Product subtitle', 'seo_ title' => 'SEO product title'] /seo-product- title/ best- products
-
- Type
- string
- Default
- "/"
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['fieldSeparator']
- Scope
- Display / Proc.
This value will divide the slug parts. If a section value contains this very value, it will be replaced by the value given in fallbackCharacter.
-
- Type
- boolean
- Default
- true
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['prefixParentPageSlug']
- Scope
- Display / Proc.
The slugs of parent pages will be prefixed to the slug for the page itself. Disable it for shorter URLs, but take the higher chance of collision into consideration.
Note
This option is exclusively for page records. It won't have an effect on any other records.
-
- Type
- array
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['regexReplacements']
- Scope
- Display / Proc.
- Added
- 14.0
A config array to provide regex based definitions for string replacement. Case-insensitive and wildcard replacement is possible.
The slug of a conference leaves out a year at the end of the title:
EXT:my_extension/Configuration/TCA/Overrides/105-tx_myextension_conference-slug.php<?php use MyVendor\MyExtension\Slug\ConferenceSlugModifier; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; defined('TYPO3') or die(); ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [ 'slug' => [ 'label' => 'my_extension.db:conference.slug', 'config' => [ 'type' => 'slug', 'generatorOptions' => [ // The short title, or the title if there is no short title 'fields' => [['short_title', 'title']], 'replacements' => [ '&' => 'and', ], 'regexReplacements' => [ // A year at the end of the title, the modifier adds it in front '/\s+\d{4}$/' => '', ], 'postModifiers' => [ ConferenceSlugModifier::class . '->prependYear', ], ], 'fallbackCharacter' => '-', 'eval' => 'uniqueInSite', ], ], ]); ExtensionManagementUtility::addToAllTCAtypes( 'tx_myextension_conference', 'slug', '', 'after:title', );Note
Pattern replacement operates on original, unsanitized field values — values are not converted to lowercase or processed. Replacement applies only to fields defined in
['generator.Options'] ['fields'] Automatic character detection or escaping is not performed, so patterns must be written carefully.
-
- Type
- array
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['replacements']
- Scope
- Display / Proc.
It allows to replace strings of a slug part. Add one of more array items with the key being the string to replace and the value being the replacement string.
EXT:my_extension/Configuration/TCA/Overrides/105-tx_myextension_conference-slug.php<?php use MyVendor\MyExtension\Slug\ConferenceSlugModifier; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; defined('TYPO3') or die(); ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [ 'slug' => [ 'label' => 'my_extension.db:conference.slug', 'config' => [ 'type' => 'slug', 'generatorOptions' => [ // The short title, or the title if there is no short title 'fields' => [['short_title', 'title']], 'replacements' => [ '&' => 'and', ], 'regexReplacements' => [ // A year at the end of the title, the modifier adds it in front '/\s+\d{4}$/' => '', ], 'postModifiers' => [ ConferenceSlugModifier::class . '->prependYear', ], ], 'fallbackCharacter' => '-', 'eval' => 'uniqueInSite', ], ], ]); ExtensionManagementUtility::addToAllTCAtypes( 'tx_myextension_conference', 'slug', '', 'after:title', );A conference titled
Tips & Tricks Dayis given a slug ending intips-instead ofand- tricks- day tips-.tricks- day The replacements are applied to each field of fields on its own, before the parts are joined with the fieldSeparator. They only apply while a slug is generated from other fields. A slug typed in by an editor is left alone.
-
- Type
- array
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['postModifiers']
- Scope
- Display / Proc.
The "slug" TCA type includes a possibility to hook into the generation of a slug via custom TCA generation options.
A hook is registered as
<class name>-><method name>, for example for the slug of a conference:EXT:my_extension/Configuration/TCA/Overrides/105-tx_myextension_conference-slug.php<?php use MyVendor\MyExtension\Slug\ConferenceSlugModifier; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; defined('TYPO3') or die(); ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [ 'slug' => [ 'label' => 'my_extension.db:conference.slug', 'config' => [ 'type' => 'slug', 'generatorOptions' => [ // The short title, or the title if there is no short title 'fields' => [['short_title', 'title']], 'replacements' => [ '&' => 'and', ], 'regexReplacements' => [ // A year at the end of the title, the modifier adds it in front '/\s+\d{4}$/' => '', ], 'postModifiers' => [ ConferenceSlugModifier::class . '->prependYear', ], ], 'fallbackCharacter' => '-', 'eval' => 'uniqueInSite', ], ], ]); ExtensionManagementUtility::addToAllTCAtypes( 'tx_myextension_conference', 'slug', '', 'after:title', );The method receives an array with the following keys:
slug- The slug to be used
workspaceId - The workspace ID,
0in the live workspace configuration- The configuration of the TCA field
record- The full record to be used
pid- The resolved parent page ID
prefix- The prefix that was added
tableName - The table of the slug field
fieldName - The name of the slug field
The modifier puts the year of the conference in front of the slug:
EXT:my_extension/Classes/Slug/ConferenceSlugModifier.php<?php declare(strict_types=1); namespace MyVendor\MyExtension\Slug; /** * Puts the year of the conference in front of the slug, for example * "2027/typo3-developer-days" */ final class ConferenceSlugModifier { /** * @param array{slug: string, record: array<string, mixed>} $parameters */ public function prependYear(array $parameters): string { $date = $parameters['record']['conference_date'] ?? ''; // A timestamp from the database, or a date such as "2027-05-12" from // the backend form $year = is_numeric($date) && (int)$date > 0 ? date('Y', (int)$date) : substr((string)$date, 0, 4); if (preg_match('/^\d{4}$/', $year) !== 1) { return $parameters['slug']; } return $year . '/' . ltrim($parameters['slug'], '/'); } }All hooks need to return the modified slug value.
The slug of a talk joins its type and its title with the field separator /:
The slug of a workshop
<?php
use MyVendor\MyExtension\Slug\TalkSlugPrefix;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [
'slug' => [
'label' => 'my_extension.db:talk.slug',
'config' => [
'type' => 'slug',
'generatorOptions' => [
// For example "workshop/write-your-first-form-element"
'fields' => ['talk_type', 'title'],
],
'appearance' => [
'prefix' => TalkSlugPrefix::class . '->getPrefix',
],
'eval' => 'uniqueInPid',
// The slug repeats the title, which the search finds already
'searchable' => false,
],
],
]);
ExtensionManagementUtility::addToAllTCAtypes(
'tx_myextension_talk',
'slug',
'',
'after:title',
);
The slug of a conference uses the short title. If the short title is empty, it uses the title:
The slug of a conference
<?php
use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
'slug' => [
'label' => 'my_extension.db:conference.slug',
'config' => [
'type' => 'slug',
'generatorOptions' => [
// The short title, or the title if there is no short title
'fields' => [['short_title', 'title']],
'replacements' => [
'&' => 'and',
],
'regexReplacements' => [
// A year at the end of the title, the modifier adds it in front
'/\s+\d{4}$/' => '',
],
'postModifiers' => [
ConferenceSlugModifier::class . '->prependYear',
],
],
'fallbackCharacter' => '-',
'eval' => 'uniqueInSite',
],
],
]);
ExtensionManagementUtility::addToAllTCAtypes(
'tx_myextension_conference',
'slug',
'',
'after:title',
);
The title 'TYPO3 Developer Days 2027' is given the slug '2027/typo3-developer-days'. The regular expression removes the year at the end of the title, and the modifier adds the year of the conference date in front:
The slug of a conference
<?php
use MyVendor\MyExtension\Slug\ConferenceSlugModifier;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
defined('TYPO3') or die();
ExtensionManagementUtility::addTCAcolumns('tx_myextension_conference', [
'slug' => [
'label' => 'my_extension.db:conference.slug',
'config' => [
'type' => 'slug',
'generatorOptions' => [
// The short title, or the title if there is no short title
'fields' => [['short_title', 'title']],
'replacements' => [
'&' => 'and',
],
'regexReplacements' => [
// A year at the end of the title, the modifier adds it in front
'/\s+\d{4}$/' => '',
],
'postModifiers' => [
ConferenceSlugModifier::class . '->prependYear',
],
],
'fallbackCharacter' => '-',
'eval' => 'uniqueInSite',
],
],
]);
ExtensionManagementUtility::addToAllTCAtypes(
'tx_myextension_conference',
'slug',
'',
'after:title',
);
-
- Type
- boolean
- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['prependSlash']
- Scope
- Display / Proc.
Defines whether a slug field should contain a prepending slash, for example for nested categories with speaking segments.
If not set, this defaults to
false. (Exception: for thepages.field this defaults toslug trueand cannot be changed.)
-
- Type
- boolean
- Default
true- TCA path
- $GLOBALS['TCA'][$table]['columns'][$field]['config']['searchable']
- Scope
- Display
New in version 14.0
TYPO3 now automatically includes all fields of suitable types in backend search operations in the Content > Records module.
This option can be used to explicitly exclude fields from being searchable, instead of having to list them in the now removed option $GLOBALS['TCA'][$table]['ctrl']['searchFields'].
By default, all fields of type
slugare considered searchable. To exclude a field from being searchable, set the following in the field's TCA configuration:EXT:my_extension/Configuration/TCA/Overrides/205-tx_myextension_talk-slug.php<?php use MyVendor\MyExtension\Slug\TalkSlugPrefix; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; defined('TYPO3') or die(); ExtensionManagementUtility::addTCAcolumns('tx_myextension_talk', [ 'slug' => [ 'label' => 'my_extension.db:talk.slug', 'config' => [ 'type' => 'slug', 'generatorOptions' => [ // For example "workshop/write-your-first-form-element" 'fields' => ['talk_type', 'title'], ], 'appearance' => [ 'prefix' => TalkSlugPrefix::class . '->getPrefix', ], 'eval' => 'uniqueInPid', // The slug repeats the title, which the search finds already 'searchable' => false, ], ], ]); ExtensionManagementUtility::addToAllTCAtypes( 'tx_myextension_talk', 'slug', '', 'after:title', );