From 55d3b029c6823cf666f0100388efbc3f22fa0d0a Mon Sep 17 00:00:00 2001 From: Christian Spoo Date: Tue, 19 May 2026 13:07:30 +0200 Subject: [PATCH 1/2] [FEATURE] Establish compatibility with TYPO3 14 --- Classes/Command/CleanupIndexCommand.php | 80 ++++++++----------- Classes/Command/ClearIndexCommand.php | 28 ++++--- Classes/Command/IndexCommand.php | 61 +++++++------- .../Command/InitializeIndexQueueCommand.php | 52 ++++++------ Classes/Command/ListSitesCommand.php | 20 +++-- composer.json | 10 +-- 6 files changed, 121 insertions(+), 130 deletions(-) diff --git a/Classes/Command/CleanupIndexCommand.php b/Classes/Command/CleanupIndexCommand.php index cd7a8bd..c22205b 100644 --- a/Classes/Command/CleanupIndexCommand.php +++ b/Classes/Command/CleanupIndexCommand.php @@ -1,22 +1,30 @@ setDescription('Delete documents in index which are not in the index queue'); } @@ -25,27 +33,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $rootPageItemTypeMap = $this->getRootPageItemTypeMap(); - /** @var SiteRepository $siteRepository */ - $siteRepository = GeneralUtility::makeInstance(SiteRepository::class); - - foreach($rootPageItemTypeMap as $item) { + foreach ($rootPageItemTypeMap as $item) { $rootPage = $item['root']; $itemType = $item['item_type']; - $site = $siteRepository->getSiteByPageId($rootPage); + $site = $this->siteRepository->getSiteByPageId($rootPage); if (!$site) { continue; } - $indexQueueItems = $this->getItemsByRootPageAndType($rootPage, $itemType); - $solrServers = GeneralUtility::makeInstance(ConnectionManager::class)->getConnectionsBySite($site); + $indexQueueItems = $this->getItemsByRootPageAndType($rootPage, $itemType); + $solrServers = $this->connectionManager->getConnectionsBySite($site); - /** @var Query $query */ $query = GeneralUtility::makeInstance(Query::class); - $query->setQuery(sprintf('type:%s AND siteHash:%s', - $itemType, - $site->getSiteHash(), - )); + $query->setQuery(sprintf('type:%s AND siteHash:%s', $itemType, $site->getSiteHash())); $query->setFields(['uid']); foreach ($solrServers as $server) { @@ -57,16 +58,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int $documentUidsToDelete = []; $i = 0; - $pages = ceil($numFound / 1000); + $pages = (int)ceil($numFound / 1000); while ($i <= $pages) { $query->addParam('start', $i * 1000); $query->addParam('rows', 1000); $response = $server->getReadService()->search($query); - /** @var Document[]|null $documents */ - $documents = $response->getParsedData()?->response?->docs; - - foreach($documents ?? [] as $document) { + $documents = $response->getParsedData()?->response?->docs ?? []; + foreach ($documents as $document) { $uid = $document->getFields()['uid']; if (!in_array($uid, $indexQueueItems)) { $documentUidsToDelete[] = $uid; @@ -77,55 +76,46 @@ protected function execute(InputInterface $input, OutputInterface $output): int } foreach ($documentUidsToDelete as $uid) { - $deleteQuery = sprintf('type:%s AND siteHash:%s AND uid:%s', - $itemType, - $site->getSiteHash(), - $uid - ); - if ($output->isVerbose()) { $output->writeln(sprintf('Deleting uid:%d (type: %s, root page: %d)', $uid, $itemType, $rootPage)); } - - $server->getWriteService()->deleteByQuery($deleteQuery); + $server->getWriteService()->deleteByQuery(sprintf( + 'type:%s AND siteHash:%s AND uid:%s', + $itemType, + $site->getSiteHash(), + $uid, + )); } $server->getWriteService()->commit(); } } - return 0; + return Command::SUCCESS; } - protected function getRootPageItemTypeMap(): array + private function getRootPageItemTypeMap(): array { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable('tx_solr_indexqueue_item'); - $map = $queryBuilder + return $this->connectionPool + ->getQueryBuilderForTable('tx_solr_indexqueue_item') ->select('root', 'item_type') ->from('tx_solr_indexqueue_item') ->groupBy('root', 'item_type') ->executeQuery() ->fetchAllAssociative(); - - return $map; } - protected function getItemsByRootPageAndType(int $rootPage, string $type): array + private function getItemsByRootPageAndType(int $rootPage, string $type): array { - /** @var QueryBuilder $queryBuilder */ - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable('tx_solr_indexqueue_item'); - $query = $queryBuilder + $queryBuilder = $this->connectionPool->getQueryBuilderForTable('tx_solr_indexqueue_item'); + return $queryBuilder ->select('item_uid') ->from('tx_solr_indexqueue_item') ->where( $queryBuilder->expr()->eq('root', $queryBuilder->createNamedParameter($rootPage, Connection::PARAM_INT)), - $queryBuilder->expr()->eq('item_type', $queryBuilder->createNamedParameter($type)) + $queryBuilder->expr()->eq('item_type', $queryBuilder->createNamedParameter($type)), ) - ->executeQuery(); - - $items = $query->fetchFirstColumn(); - return $items; + ->executeQuery() + ->fetchFirstColumn(); } } diff --git a/Classes/Command/ClearIndexCommand.php b/Classes/Command/ClearIndexCommand.php index 68b9a42..44ff16a 100644 --- a/Classes/Command/ClearIndexCommand.php +++ b/Classes/Command/ClearIndexCommand.php @@ -1,5 +1,7 @@ setDescription('Clear index') - ->addArgument('rootpage', InputArgument::REQUIRED, 'The root page id') - ->addArgument('type', InputArgument::OPTIONAL, 'A type. Normally a table name'); + ->addArgument('rootpage', InputArgument::REQUIRED, 'The root page id') + ->addArgument('type', InputArgument::OPTIONAL, 'A type. Normally a table name'); } protected function execute(InputInterface $input, OutputInterface $output): int { - /** @var SiteRepository $siteRepository */ - $siteRepository = GeneralUtility::makeInstance(SiteRepository::class); - - $site = $siteRepository->getSiteByPageId($input->getArgument('rootpage')); + $site = $this->siteRepository->getSiteByPageId((int)$input->getArgument('rootpage')); if (!$site instanceof Site) { $output->writeln('Site not found'); - return 1; + return Command::FAILURE; } - $connectionManager = GeneralUtility::makeInstance(ConnectionManager::class); - foreach ($connectionManager->getConnectionsBySite($site) as $connection) { + foreach ($this->connectionManager->getConnectionsBySite($site) as $connection) { if ($type = $input->getArgument('type')) { $connection->getWriteService()->deleteByType($type); } else { @@ -41,6 +45,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $connection->getWriteService()->commit(); } - return 0; + return Command::SUCCESS; } } diff --git a/Classes/Command/IndexCommand.php b/Classes/Command/IndexCommand.php index ed3693b..500c35d 100644 --- a/Classes/Command/IndexCommand.php +++ b/Classes/Command/IndexCommand.php @@ -1,9 +1,11 @@ addArgument('rootpage', InputArgument::OPTIONAL, 'Site root page id', '*'); - $this->addArgument('limit', InputArgument::OPTIONAL, 'Limit documents per site') - ->setDescription('Index documents of all sites'); + $this->setDescription('Index documents of all sites') + ->addArgument('rootpage', InputArgument::OPTIONAL, 'Site root page id', '*') + ->addArgument('limit', InputArgument::OPTIONAL, 'Limit documents per site'); } protected function execute(InputInterface $input, OutputInterface $output): int { - /** @var SiteRepository $siteRepository */ - $siteRepository = GeneralUtility::makeInstance(SiteRepository::class); - - if (($input->getArgument('rootpage') ?? '*') == '*') { - $sites = $siteRepository->getAvailableSites(); + $rootpage = $input->getArgument('rootpage') ?? '*'; + if ($rootpage === '*') { + $sites = $this->siteRepository->getAvailableSites(); } else { - $sites = []; - $site = $siteRepository->getSiteByPageId((integer)$input->getArgument('rootpage')); - if ($site) { - $sites[] = $site; - } + $site = $this->siteRepository->getSiteByPageId((int)$rootpage); + $sites = $site ? [$site] : []; } - if ($input->hasArgument('limit') && (integer)$input->getArgument('limit') > 0) { - $limit = (integer)$input->getArgument('limit'); - } else { - $limit = PHP_INT_MAX; - } + $limitArg = (int)($input->getArgument('limit') ?? 0); + $limit = $limitArg > 0 ? $limitArg : PHP_INT_MAX; $oneTaskFailed = false; foreach ($sites as $site) { if ($output->isVerbose()) { - $output->writeln( - sprintf( - 'Indexing %s (Root page %d)', - $site->getTypo3SiteObject()->getBase(), - $site->getRootPageId() - ) - ); + $output->writeln(sprintf( + 'Indexing %s (Root page %d)', + $site->getTypo3SiteObject()->getBase(), + $site->getRootPageId(), + )); } - $indexTask = GeneralUtility::makeInstance(IndexQueueWorkerTask::class); - $indexTask->setRootPageId($site->getRootPageId()); - $indexTask->setDocumentsToIndexLimit($limit); - - if (!$indexTask->execute()) { + $indexService = GeneralUtility::makeInstance(IndexService::class, $site); + if (!$indexService->indexItems($limit)) { $oneTaskFailed = true; $output->writeln(sprintf('Failed indexing site %d', $site->getRootPageId())); } } - return $oneTaskFailed === true ? Command::FAILURE : Command::SUCCESS; + return $oneTaskFailed ? Command::FAILURE : Command::SUCCESS; } } diff --git a/Classes/Command/InitializeIndexQueueCommand.php b/Classes/Command/InitializeIndexQueueCommand.php index 7480dd4..0790ec2 100644 --- a/Classes/Command/InitializeIndexQueueCommand.php +++ b/Classes/Command/InitializeIndexQueueCommand.php @@ -1,9 +1,11 @@ addArgument('rootpage', InputArgument::OPTIONAL, 'site root page id', '*'); - $this->addArgument('type', InputArgument::OPTIONAL, 'List of indexing configurations. Leave empty for all') - ->setDescription('Initialize index queue by type and site'); + $this->setDescription('Initialize index queue by type and site') + ->addArgument('rootpage', InputArgument::OPTIONAL, 'Site root page id', '*') + ->addArgument('type', InputArgument::OPTIONAL, 'List of indexing configurations. Leave empty for all'); } protected function execute(InputInterface $input, OutputInterface $output): int { - /** @var SiteRepository $siteRepository */ - $siteRepository = GeneralUtility::makeInstance(SiteRepository::class); - - if ($input->getArgument('rootpage') != '*') { - $sites = []; - $site = $siteRepository->getSiteByPageId((integer)$input->getArgument('rootpage')); - if ($site) { - $sites[] = $site; - } + $rootpage = $input->getArgument('rootpage') ?? '*'; + if ($rootpage !== '*') { + $site = $this->siteRepository->getSiteByPageId((int)$rootpage); + $sites = $site ? [$site] : []; } else { - $sites = $siteRepository->getAvailableSites(); + $sites = $this->siteRepository->getAvailableSites(); } $indexingConfigurations = GeneralUtility::trimExplode(',', $input->getArgument('type') ?? '*'); - /** @var Queue $indexQueue */ - $indexQueue = GeneralUtility::makeInstance(Queue::class); - // Even unknown configurations return true. This should better be fixed in EXT:solr. - $wasSuccesful = 0; + $failed = false; foreach ($sites as $site) { - $initializationStatus = $indexQueue->getQueueInitializationService()->initializeBySiteAndIndexConfigurations($site, $indexingConfigurations); + $initializationStatus = $this->queueInitializationService->initializeBySiteAndIndexConfigurations( + $site, + $indexingConfigurations, + ); - // Even unknown configurations return true. This should better be fixed in EXT:solr. - $wasSuccesful = 0; foreach ($initializationStatus as $name => $status) { - if($status === false) { - $wasSuccesful = 1; + if ($status === false) { + $failed = true; $output->writeln(sprintf('Failed for configuration %s', $name)); } } } - return $wasSuccesful; + return $failed ? Command::FAILURE : Command::SUCCESS; } } diff --git a/Classes/Command/ListSitesCommand.php b/Classes/Command/ListSitesCommand.php index 285620e..14c5971 100644 --- a/Classes/Command/ListSitesCommand.php +++ b/Classes/Command/ListSitesCommand.php @@ -1,5 +1,7 @@ setDescription('List sites'); } protected function execute(InputInterface $input, OutputInterface $output): int { - /** @var SiteRepository $siteRepository */ - $siteRepository = GeneralUtility::makeInstance(SiteRepository::class); - - $sites = $siteRepository->getAvailableSites(); - usort($sites, function(Site $a, Site $b) { + $sites = $this->siteRepository->getAvailableSites(); + usort($sites, static function (Site $a, Site $b) { return strcmp($a->getLabel(), $b->getLabel()); }); foreach ($sites as $site) { $output->writeln($site->getLabel()); } - return 0; + return Command::SUCCESS; } } diff --git a/composer.json b/composer.json index 0bc578c..beb727f 100644 --- a/composer.json +++ b/composer.json @@ -6,8 +6,8 @@ "GPL-2.0-or-later" ], "require": { - "apache-solr-for-typo3/solr": "^13 || 13.0.x-dev", - "typo3/cms-core": "^13.4" + "apache-solr-for-typo3/solr": "^14.0.0-beta1", + "typo3/cms-core": "^14.3" }, "autoload": { "psr-4": { @@ -19,11 +19,7 @@ }, "extra": { "typo3/cms": { - "extension-key": "solr_commands", - "web-dir": ".Build/Web" - }, - "branch-alias": { - "dev-master": "13.4.x-dev" + "extension-key": "solr_commands" } }, "config": { From 1a5a662062a63c5e4984b8971e62f57ad6481fc2 Mon Sep 17 00:00:00 2001 From: Christian Spoo Date: Tue, 19 May 2026 13:17:37 +0200 Subject: [PATCH 2/2] [TASK] Test against PHP 8.2 till 8.5 --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4961612..cc95c0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,8 @@ jobs: # See version matrix @ https://typo3.org/cms/roadmap - { php: 8.2 } - { php: 8.3 } + - { php: 8.4 } + - { php: 8.5 } env: ${{ matrix.env }}