Feature: #94499 - Implement AddPageTypeZeroSource event listener
See forge#94499
Description
A new event listener for \TYPO3\CMS\Redirects\Event\SlugRedirectChangeItemCreatedEvent
is introduced, which creates a \TYPO3\CMS\Redirects\RedirectUpdate\PageTypeSource for a page
before the slug has been changed. The full URI is built to fill the source_
and source_
, which takes configured Route
and Route
into account, for example, the Page
.
Note
If source_
and source_
lead to the same outcome for page type 0
using full URI building, like the \TYPO3\
, the
Plain
is replaced with the Page
.
It is not possible to configure page types for which sources should be added. If
you need to do so, read additional PageTypeSource auto-create redirect source type
which provides an example of how to implement custom event listeners based on
Page
.
If Page
for page type 0
results in a different
source, the Plain
is not removed to keep the original
behaviour, which some instances may rely on.
This behaviour can be modified by adding an event listener for SlugRedirectChangeItemCreatedEvent
Remove plain slug source if page type 0 differs:
Registration of the event in your extension's Services.
:
MyExtension\MyPackage\Redirects\MyEventListener:
tags:
- name: event.listener
identifier: 'my-extension/custom-page-type-redirect'
# Registering after core listener is important, otherwise we would
# not know if there is a PageType source for page type 0
after: 'redirects-add-page-type-zero-source'
The corresponding event listener class:
namespace MyVendor\MyExtension\Redirects;
use TYPO3\CMS\Redirects\Event\SlugRedirectChangeItemCreatedEvent;
use TYPO3\CMS\Redirects\RedirectUpdate\PageTypeSource;
use TYPO3\CMS\Redirects\RedirectUpdate\PlainSlugReplacementRedirectSource;
use TYPO3\CMS\Redirects\RedirectUpdate\RedirectSourceCollection;
use TYPO3\CMS\Redirects\RedirectUpdate\RedirectSourceInterface;
final class MyEventListener
{
public function __invoke(
SlugRedirectChangeItemCreatedEvent $event
): void {
$changeItem = $event->getSlugRedirectChangeItem();
$sources = $changeItem->getSourcesCollection()->all();
$pageTypeZeroSource = $this->getPageTypeZeroSource(
...array_values($sources)
);
if ($pageTypeZeroSource === null) {
// nothing we can do - no page type 0 source found
return;
}
// Remove plain slug replacement redirect source from sources. We
// already know, that if it is there it differs from the page type
// 0 source, therefor it is safe to simply remove it by class check.
$sources = array_filter(
$sources,
static fn ($source) => !($source instanceof PlainSlugReplacementRedirectSource)
);
// update sources
$changeItem = $changeItem->withSourcesCollection(
new RedirectSourceCollection(
...array_values($sources)
)
);
// update change item with updated sources
$event->setSlugRedirectChangeItem($changeItem);
}
private function getPageTypeZeroSource(
RedirectSourceInterface ...$sources
): ?PageTypeSource {
foreach ($sources as $source) {
if ($source instanceof PageTypeSource
&& $source->getPageType() === 0
) {
return $source;
}
}
return null;
}
}
Impact
An additional redirect source is automatically added if a Page
is configured in the Site
for page type 0
. In that case
two redirects are created, one for the plain slug change and one with the suffix
in the source_
. That way it does not break instances relying on the
fact that plain slug based redirects are created.
Note
This behaviour can be modified by adding an event listener for
SlugRedirectChangeItemCreatedEvent.
It can check if both variants are in the source collection and remove the
Plain
, as found in the example above.