ext-solr
ext-solr copied to clipboard
Indexing pages below a backend user section leads to errors
If a standard page (or any other indexable page) is below a page of type backend user section this leads to indexing error. The error message in the TYPO3 log is
Uncaught TYPO3 Exception: #1517584045: Solr Document can not be prepared. The Reason: ID was not an accessible page
This is correct so far, as these contents may not be indexed, but also they should not occur as errors.
I wrote this event listener to solve this issue for our projects. Seems to do the trick.
<?php
declare(strict_types=1);
namespace Vendor\CustomSolr\Solr\EventListener;
use ApacheSolrForTypo3\Solr\Event\IndexQueue\AfterIndexQueueHasBeenInitializedEvent;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Exception;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
final readonly class IndexQueueInitializationEventListener
{
/**
* @throws Exception
*/
public function __invoke(AfterIndexQueueHasBeenInitializedEvent $event): void
{
if ($event->getIndexingConfigurationName() !== 'pages') {
return;
}
$site = $event->getSite();
$rootPageId = $site->getRootPageId();
$this->removeBackendUserSectionSubpages($rootPageId);
}
/**
* @throws Exception
*/
private function removeBackendUserSectionSubpages(int $rootPageId): void
{
$backendUserSectionPageIds = $this->findBackendUserSectionPages($rootPageId);
if (empty($backendUserSectionPageIds)) {
return;
}
$subpageIds = [];
foreach ($backendUserSectionPageIds as $pageId) {
$subpageIds = array_merge($subpageIds, $this->findSubpagesOfPage($pageId));
}
if (empty($subpageIds)) {
return;
}
$this->removeItemsFromIndexQueue($subpageIds);
}
/**
* @throws Exception
*/
private function findBackendUserSectionPages(int $rootPageId): array
{
/** @var QueryBuilder $queryBuilder */
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('pages');
$result = $queryBuilder
->select('uid')
->from('pages')
->where(
$queryBuilder->expr()->eq('doktype', $queryBuilder->createNamedParameter(6, \PDO::PARAM_INT)),
$queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
)
->executeQuery()
->fetchAllAssociative();
$potentialBackendUserSectionPageIds = array_map(static function(array $page) {
return (int)$page['uid'];
}, $result);
$backendUserSectionPageIds = [];
foreach ($potentialBackendUserSectionPageIds as $pageId) {
$isInSite = $this->isPageInSiteRootline($pageId, $rootPageId);
if ($isInSite) {
$backendUserSectionPageIds[] = $pageId;
}
}
return $backendUserSectionPageIds;
}
/**
* @throws Exception
*/
private function isPageInSiteRootline(int $pageId, int $rootPageId): bool
{
if ($pageId === $rootPageId) {
return true;
}
$connection = GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable('pages');
$currentPageId = $pageId;
$iterations = 0;
while ($currentPageId > 0 && $iterations < 100) {
$record = $connection->select(
['pid'],
'pages',
['uid' => $currentPageId, 'deleted' => 0]
)->fetchAssociative();
if ($record === false) {
return false;
}
$currentPageId = (int)$record['pid'];
if ($currentPageId === $rootPageId) {
return true;
}
$iterations++;
}
return false;
}
/**
* @throws Exception
*/
private function findSubpagesOfPage(int $pageId): array
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('pages');
$allSubpages = [];
$pagesToProcess = [$pageId];
$processedPages = [];
while (!empty($pagesToProcess)) {
$currentPageId = array_shift($pagesToProcess);
if (in_array($currentPageId, $processedPages, true)) {
continue;
}
$processedPages[] = $currentPageId;
$result = $queryBuilder->select('uid')
->from('pages')
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($currentPageId, \PDO::PARAM_INT)),
$queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
)
->executeQuery()
->fetchAllAssociative();
foreach ($result as $page) {
$childPageId = (int)$page['uid'];
$allSubpages[] = $childPageId;
$pagesToProcess[] = $childPageId;
}
}
return $allSubpages;
}
private function removeItemsFromIndexQueue(array $pageIds): void
{
if (empty($pageIds)) {
return;
}
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tx_solr_indexqueue_item');
$queryBuilder
->delete('tx_solr_indexqueue_item')
->where(
$queryBuilder->expr()->eq('item_type', $queryBuilder->createNamedParameter('pages')),
$queryBuilder->expr()->in(
'item_uid',
$queryBuilder->createNamedParameter($pageIds, ArrayParameterType::INTEGER)
)
)
->executeStatement();
}
}
Services.yaml
Vendor\CustomSolr\Solr\EventListener\IndexQueueInitializationEventListener:
tags:
- name: event.listener
identifier: 'removeBackendUserSectionSubpages'
event: ApacheSolrForTypo3\Solr\Event\IndexQueue\AfterIndexQueueHasBeenInitializedEvent