Breaking: #110334 - XML sitemap data provider interface changed 

See forge#110334

Description 

\TYPO3\CMS\Seo\XmlSitemap\XmlSitemapDataProviderInterface declared a constructor, which forced every data provider to be instantiated with its runtime information as constructor arguments. Data providers could therefore never be resolved by the dependency injection container and had to fetch all their dependencies statically. In addition, the items of a sitemap had to be collected in the constructor, since the interface methods took no arguments.

The interface has been changed accordingly:

  • The constructor declaration and the methods getKey() , getItems() , getLastModified() and getNumberOfPages() have been removed in favor of the single method getSitemap() .
  • getSitemap() receives all runtime information as \TYPO3\CMS\Seo\XmlSitemap\XmlSitemapRequest : The name of the requested sitemap, its configuration, the number of the requested page, the current request and a content object renderer to generate URLs with.
  • getSitemap() returns a \TYPO3\CMS\Seo\XmlSitemap\XmlSitemap carrying the items of the requested page, the last modification date and the number of pages of the sitemap.
  • Data providers are stateless services now. The interface is tagged with seo.xmlsitemap.provider , so implementations are registered in the dependency injection container automatically and are free to use constructor injection.

Registering a sitemap in TypoScript is unchanged: Data providers are still referenced by their class name.

Impact 

Classes implementing XmlSitemapDataProviderInterface without the method getSitemap() will cause a fatal PHP error.

Data providers that are not available as a service in the dependency injection container are not resolved anymore: Rendering such a sitemap throws a \TYPO3\CMS\Seo\XmlSitemap\Exception\InvalidConfigurationException , and the sitemap is skipped in the sitemap index.

Data providers extending \TYPO3\CMS\Seo\XmlSitemap\AbstractXmlSitemapDataProvider keep working unchanged, see Deprecation: #110334 - AbstractXmlSitemapDataProvider.

Affected installations 

All installations with custom extensions implementing XmlSitemapDataProviderInterface directly. This is a rarely used API, most data providers extend AbstractXmlSitemapDataProvider instead.

Migration 

Move the collecting of items from the constructor to getSitemap() and take the configuration, the requested page and the content object renderer from the XmlSitemapRequest . The constructor is free for dependency injection afterwards:

EXT:my_extension/Classes/XmlSitemap/MyXmlSitemapDataProvider.php
use TYPO3\CMS\Seo\XmlSitemap\XmlSitemap;
use TYPO3\CMS\Seo\XmlSitemap\XmlSitemapDataProviderInterface;
use TYPO3\CMS\Seo\XmlSitemap\XmlSitemapRequest;

final readonly class MyXmlSitemapDataProvider implements XmlSitemapDataProviderInterface
{
    public function __construct(
        private MyItemRepository $itemRepository,
    ) {}

    public function getSitemap(XmlSitemapRequest $sitemapRequest): XmlSitemap
    {
        $items = [];
        foreach ($this->itemRepository->findAll($sitemapRequest->configuration) as $item) {
            $items[] = [
                'uid' => $item['uid'],
                'lastMod' => $item['tstamp'],
            ];
        }
        return XmlSitemap::forPage(
            $items,
            $sitemapRequest->page,
            fn(array $item): array => $this->defineUrl($item, $sitemapRequest),
        );
    }

    private function defineUrl(array $item, XmlSitemapRequest $sitemapRequest): array
    {
        $item['loc'] = $sitemapRequest->contentObjectRenderer->createUrl([
            'parameter' => $item['uid'],
            'forceAbsoluteUrl' => 1,
        ]);
        return $item;
    }
}
Copied!

XmlSitemap::forPage() takes all items of a sitemap, extracts the items of the requested page and calculates the last modification date and the number of pages needed by the sitemap index. The optional item mapper is applied to the items of the requested page only, and only when the items are rendered at all - the sitemap index does not generate any URL this way. Data providers taking care of paging themselves, for example by limiting their database query to the items of the requested page, use XmlSitemap::create() instead:

return XmlSitemap::create(
    fn(): array => $this->itemRepository->findForPage($sitemapRequest->page),
    $this->itemRepository->findLastModified(),
    $this->itemRepository->countPages(),
);
Copied!

In case the extension supports both TYPO3 v14 and v15, extend AbstractXmlSitemapDataProvider instead of implementing the interface directly: Such a data provider is instantiated with its runtime information in v15 as well, at the cost of a deprecation message. It is then migrated to the new interface once support for TYPO3 v14 is dropped.