*/ 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); } }