Validate links of a custom link handler (t3://...) 

The link validator does not scan the content itself. It evaluates the result of the soft reference parsers configured in the TCA of a field, for example typolink_tag for tt_content.bodytext and typolink for link fields like tt_content.header_link .

Both parsers know the core link types such as pages, files, URLs and records. For all other link types, for example a custom link handler providing links like t3://shop?identifier=123, they dispatch the PSR-14 event \TYPO3\CMS\Core\DataHandling\Event\AppendLinkHandlerElementsEvent .

To validate such links, two parts are needed:

  1. An event listener which turns the link into an entry for the link validator.
  2. A linktype which checks the entry.

No custom soft reference parser and no change of the TCA are required, as long as the field already uses typolink_tag or typolink .

Event listener 

The listener returns an element whose subst array describes the link. The type is matched by the linktype, the tokenValue is passed to LinktypeInterface->checkLink() as the URL to check.

packages/my_extension/Classes/EventListener/AppendShopLinkElements.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\EventListener;

use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\DataHandling\Event\AppendLinkHandlerElementsEvent;

#[AsEventListener]
final class AppendShopLinkElements
{
    public function __invoke(AppendLinkHandlerElementsEvent $event): void
    {
        $linkParts = $event->getLinkParts();
        // Link details of "t3://shop?..." as built by the link handler
        if (($linkParts['type'] ?? '') !== 'shop') {
            return;
        }

        $tokenId = $event->getTokenId();
        $event->addElements([
            $tokenId . ':' . $event->getIdx() => [
                'matchString' => $event->getContent(),
                'subst' => [
                    'type' => 'shop',
                    'tokenID' => $tokenId,
                    'tokenValue' => (string)($linkParts['identifier'] ?? ''),
                ],
            ],
        ]);
    }
}
Copied!

Calling addElements() marks the event as resolved. Links without a resolved event are skipped by typolink_tag and reported as "Couldn't decide typolink mode." by typolink .

Linktype 

packages/my_extension/Classes/Linktype/ShopLinktype.php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\Linktype;

use TYPO3\CMS\Linkvalidator\LinkAnalyzer;
use TYPO3\CMS\Linkvalidator\Linktype\AbstractLinktype;

final class ShopLinktype extends AbstractLinktype
{
    protected string $identifier = 'shop';

    public function checkLink(string $url, array $softRefEntry, LinkAnalyzer $reference): bool
    {
        // $url is the "tokenValue" set in the event listener
        return $this->productExists($url);
    }

    private function productExists(string $identifier): bool
    {
        // Ask your shop API or database
        return true;
    }
}
Copied!

The default AbstractLinktype->fetchType() selects the linktype if the type of the entry equals the identifier of the linktype, here shop. Register and activate the linktype as described in Implementation of a custom linktype for the link validator:

packages/my_extension/Configuration/page.tsconfig
mod.linkvalidator.linktypes = db,file,external,shop
Copied!

Fields without the typolink soft reference parsers 

If a field does not use typolink_tag or typolink , add one of them to the softref option of the field in the TCA, for example:

packages/my_extension/Configuration/TCA/Overrides/tt_content.php
$GLOBALS['TCA']['tt_content']['columns']['my_field']['config']['softref'] = 'typolink';
Copied!