TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Backend\Search\LiveSearch;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use TYPO3\CMS\Backend\Search\Event\ModifyResultItemInLiveSearchEvent;
|
||||
use TYPO3\CMS\Backend\Search\LiveSearch\SearchDemand\DemandPropertyName;
|
||||
use TYPO3\CMS\Backend\Search\LiveSearch\SearchDemand\MutableSearchDemand;
|
||||
use TYPO3\CMS\Backend\Search\LiveSearch\SearchDemand\SearchDemand;
|
||||
use TYPO3\CMS\Core\Pagination\ArrayPaginator;
|
||||
|
||||
/**
|
||||
* Repository class to ease using the search API.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final readonly class SearchRepository
|
||||
{
|
||||
public function __construct(
|
||||
private EventDispatcherInterface $eventDispatcher,
|
||||
private SearchProviderRegistry $searchProviderRegistry,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Returns a list of available search providers including a flag whether they are currently active.
|
||||
*
|
||||
* @param SearchDemand $searchDemand
|
||||
* @return array<class-string, array{instance: SearchProviderInterface, isActive: bool}>
|
||||
*/
|
||||
public function getSearchProviderState(SearchDemand $searchDemand): array
|
||||
{
|
||||
$searchProviders = [];
|
||||
foreach ($this->searchProviderRegistry->getProviders() as $searchProviderClassName => $searchProvider) {
|
||||
$searchProviders[$searchProviderClassName] = [
|
||||
'instance' => $searchProvider,
|
||||
'isActive' => in_array($searchProviderClassName, $searchDemand->getSearchProviders(), true),
|
||||
];
|
||||
}
|
||||
|
||||
return $searchProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SearchProviderInterface[]
|
||||
*/
|
||||
public function getViableSearchProviders(SearchDemand $searchDemand): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->searchProviderRegistry->getProviders(),
|
||||
static fn(SearchProviderInterface $provider): bool => $searchDemand->getSearchProviders() === [] || in_array(get_class($provider), $searchDemand->getSearchProviders(), true)
|
||||
);
|
||||
}
|
||||
|
||||
public function find(SearchDemand $searchDemand): ArrayPaginator
|
||||
{
|
||||
$searchResults = [];
|
||||
$totalCount = 0;
|
||||
$mutableSearchDemand = MutableSearchDemand::fromSearchDemand($searchDemand);
|
||||
$offset = $searchDemand->getOffset();
|
||||
$remainingItems = $searchDemand->getLimit();
|
||||
|
||||
foreach ($this->getViableSearchProviders($searchDemand) as $provider) {
|
||||
// Initialize remaining-items and offset for current iteration
|
||||
$mutableSearchDemand
|
||||
->setProperty(DemandPropertyName::limit, $remainingItems)
|
||||
->setProperty(DemandPropertyName::offset, $offset);
|
||||
|
||||
$count = $provider->count($mutableSearchDemand->freeze());
|
||||
// Total count is relevant outside of this loop:
|
||||
// Paginator calculates number of pages and fills up its result set with stub-entries.
|
||||
$totalCount += $count;
|
||||
|
||||
if ($count < $offset) {
|
||||
// The number of potential results is smaller than the offset, do not query results
|
||||
$offset -= $count;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($remainingItems < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$providerResult = $provider->find($mutableSearchDemand->freeze());
|
||||
if ($providerResult !== []) {
|
||||
foreach ($providerResult as $key => $resultItem) {
|
||||
$modifyRecordEvent = $this->eventDispatcher->dispatch(new ModifyResultItemInLiveSearchEvent($resultItem));
|
||||
$providerResult[$key] = $modifyRecordEvent->getResultItem();
|
||||
}
|
||||
$remainingItems -= count($providerResult);
|
||||
// We have got a first usable result from the current SearchProvider here. The offset is thereby fulfilled.
|
||||
// All follow-up SearchProviders must run with offset=0 to start with their first item:
|
||||
// * to generate a correct item-count and
|
||||
// * to provide more items if there are still open remaining-items.
|
||||
$offset = 0;
|
||||
|
||||
$searchResults[] = $providerResult;
|
||||
}
|
||||
}
|
||||
unset($mutableSearchDemand);
|
||||
|
||||
$flattenedSearchResults = array_merge([], ...$searchResults);
|
||||
$resultCount = count($flattenedSearchResults);
|
||||
|
||||
$currentPage = (int)floor(($searchDemand->getOffset() + $searchDemand->getLimit()) / $searchDemand->getLimit());
|
||||
if (ceil($totalCount / $searchDemand->getLimit()) < $currentPage) {
|
||||
// Requested page does not match with the overall amount of items, reset to first page
|
||||
$currentPage = 1;
|
||||
}
|
||||
|
||||
if ($resultCount > 0) {
|
||||
// The paginator expects a full result set to be able to calculate its pagination. This will have negative
|
||||
// performance consequences, therefore we only consider the current result set and create stubs for the "gaps".
|
||||
$paginatorItems = array_merge(
|
||||
array_fill(0, $searchDemand->getOffset(), null),
|
||||
$flattenedSearchResults,
|
||||
array_fill(0, $totalCount - $searchDemand->getOffset() - $resultCount, null)
|
||||
);
|
||||
} else {
|
||||
$paginatorItems = [];
|
||||
}
|
||||
|
||||
return new ArrayPaginator($paginatorItems, $currentPage, SearchDemand::DEFAULT_LIMIT);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user