BeforeRecordIsAnalyzedEvent¶
Event that is fired to modify results (= add results) or modify the record before the linkanalyzer analyzes the record.
Example¶
In this example we are checking if there are external links containing the URL of the project itself as editors tend to set external links on internal pages at times.
The following code can be put in a custom minimal extension. You find a live example in our example extension EXT:examples.
Create a class that works as event listener. This class does not implement or
extend any class. It has to provide a method that accepts an event of type
TYPO3\CMS\Linkvalidator\Event\BeforeRecordIsAnalyzedEvent
. By default
the method is called __invoke
:
use TYPO3\CMS\Linkvalidator\Event\BeforeRecordIsAnalyzedEvent;
final class CheckExternalLinksToLocalPagesEventListener
{
private const LOCAL_DOMAIN = 'example.org';
private const TABLE_NAME = 'tt_content';
private const FIELD_NAME = 'bodytext';
public function __invoke(BeforeRecordIsAnalyzedEvent $event): void
{
$table = $event->getTableName();
if ($table !== self::TABLE_NAME) {
return;
}
$results = $event->getResults();
$record = $event->getRecord();
$field = (string)$record[self::FIELD_NAME];
if (!str_contains($field, self::LOCAL_DOMAIN)) {
return;
}
$results = $this->parseField($record, $results);
$event->setResults($results);
}
}
The listener must then be registered in the extensions Services.yaml
:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
T3docs\Examples\:
resource: '../Classes/*'
exclude: '../Classes/Domain/Model/*'
T3docs\Examples\EventListener\LinkValidator\CheckExternalLinksToLocalPagesEventListener:
tags:
- name: event.listener
identifier: 'txExampleCheckExternalLinksToLocalPages'
For the implementation we need the BrokenLinkRepository
to register
additional link errors and the SoftReferenceParserFactory
so we can
automatically parse for links. These two classes have to be injected via
dependeny injection:
use TYPO3\CMS\Core\DataHandling\SoftReference\SoftReferenceParserFactory;
use TYPO3\CMS\Linkvalidator\Repository\BrokenLinkRepository;
final class CheckExternalLinksToLocalPagesEventListener
{
private BrokenLinkRepository $brokenLinkRepository;
private SoftReferenceParserFactory $softReferenceParserFactory;
public function __construct(
BrokenLinkRepository $brokenLinkRepository,
SoftReferenceParserFactory $softReferenceParserFactory
) {
$this->brokenLinkRepository = $brokenLinkRepository;
$this->softReferenceParserFactory = $softReferenceParserFactory;
}
}
Now we use the SoftReferenceParserFactory
to find all registered link
parsers for soft reference. Then we apply each of these parsers in turn to
the configured field in the current record. For each link found we can now
match if it is an external link to an internal page.
use TYPO3\CMS\Core\DataHandling\SoftReference\SoftReferenceParserFactory;
final class CheckExternalLinksToLocalPagesEventListener
{
private const LOCAL_DOMAIN = 'example.org';
private const TABLE_NAME = 'tt_content';
private const FIELD_NAME = 'bodytext';
private SoftReferenceParserFactory $softReferenceParserFactory;
private function parseField(array $record, array $results): array
{
$conf = $GLOBALS['TCA'][self::TABLE_NAME]['columns'][self::FIELD_NAME]['config'];
foreach ($this->findAllParsers($conf) as $softReferenceParser) {
$parserResult = $softReferenceParser->parse(
self::TABLE_NAME,
self::FIELD_NAME,
$record['uid'],
(string)$record[self::FIELD_NAME]
);
if (!$parserResult->hasMatched()) {
continue;
}
foreach ($parserResult->getMatchedElements() as $matchedElement) {
if (!isset($matchedElement['subst'])) {
continue;
}
$this->matchUrl(
(string)$matchedElement['subst']['tokenValue'],
$record,
$results
);
}
}
return $results;
}
private function findAllParsers(array $conf): iterable
{
return $this->softReferenceParserFactory->getParsersBySoftRefParserList(
$conf['softref'],
['subst']
);
}
}
If the URL found in the matching is external and contains the local domain name
we add the an entry to the BrokenLinkRepository
and to the result set of
BeforeRecordIsAnalyzedEvent
.
use TYPO3\CMS\Linkvalidator\Repository\BrokenLinkRepository;
final class CheckExternalLinksToLocalPagesEventListener
{
private const LOCAL_DOMAIN = 'example.org';
private const TABLE_NAME = 'tt_content';
private const FIELD_NAME = 'bodytext';
private BrokenLinkRepository $brokenLinkRepository;
private function matchUrl(string $foundUrl, array $record, array &$results): void
{
if (str_contains($foundUrl, self::LOCAL_DOMAIN)) {
$this->addItemToBrokenLinkRepository($record, $foundUrl);
$results[] = $record;
}
}
private function addItemToBrokenLinkRepository(array $record, string $foundUrl): void
{
$link = [
'record_uid' => $record['uid'],
'record_pid' => $record['pid'],
'language' => $record['sys_language_uid'],
'field' => self::FIELD_NAME,
'table_name' => self::TABLE_NAME,
'url' => $foundUrl,
'last_check' => time(),
'link_type' => 'external',
];
$this->brokenLinkRepository->addBrokenLink($link, false, [
'errorType' => 'exception',
'exception' => 'Do not link externally to ' . self::LOCAL_DOMAIN,
'errno' => 1661517573,
]);
}
}
The BrokenLinkRepository
is not an Extbase repository but a repository
based on the Doctrine database abstraction (DBAL).
It therefore expects an array with the names of the table fields as argument and
not an Extbase model. The method internally uses
TYPO3\CMS\Core\Database\Connection::insert
. This method automatically
quotes all identifiers and values, we therefore do not worry about escaping here.
Tip
It is recommended to always use a unique error number. An easy way to ensure the error number to be unique is to use the current Unix timestamp of the time of writing the code.
See the complete class here: CheckExternalLinksToLocalPagesEventListener.
API¶
- class TYPO3\CMS\Linkvalidator\Event\BeforeRecordIsAnalyzedEvent¶
Event that is fired to modify results (= add results) or modify the record before the linkanalyzer analyzes the record.
- getTableName()¶
- Return type
string
- getRecord()¶
- Return type
array
- setRecord(array $record)¶
- Parameters
$record (
array
) -- the record
- getFields()¶
- Return type
array
- getResults()¶
- Return type
array
- setResults(array $results)¶
- Parameters
$results (
array
) -- the results
- getLinkAnalyzer()¶
- Return type
TYPO3\CMS\Linkvalidator\LinkAnalyzer