Skip to content

Commit

Permalink
[!!!][FEATURE] Rework Search Component system
Browse files Browse the repository at this point in the history
All Search Components have been reworked and the Component
system have been moved to a PSR-14 Event
`AfterSearchQueryHasBeenPreparedEvent`.

This way, the registration works just as other PSR-14 events,
and Dependency Injection is now used consistently across all
Componenents.

The Component System is now merged with the previous hook
`$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifySearchQuery']`
and the `ApacheSolrForTypo3\Solr\Search\Modifier` interface.

Two other hooks have been migrated as well.

Tests who actually tested the registration have been removed,
further existing tests have been adapted.

Fixes: TYPO3-Solr#3675
  • Loading branch information
bmack authored and dkd-kaehm committed Jun 5, 2023
1 parent 1a926ce commit 71fe543
Show file tree
Hide file tree
Showing 40 changed files with 932 additions and 1,555 deletions.
66 changes: 0 additions & 66 deletions Classes/Domain/Search/LastSearches/LastSearchesWriterProcessor.php

This file was deleted.

122 changes: 20 additions & 102 deletions Classes/Domain/Search/ResultSet/SearchResultSetService.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,20 @@
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Result\SearchResult;
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Result\SearchResultBuilder;
use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest;
use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequestAware;
use ApacheSolrForTypo3\Solr\Domain\Variants\VariantsProcessor;
use ApacheSolrForTypo3\Solr\Query\Modifier\Modifier;
use ApacheSolrForTypo3\Solr\Event\Search\AfterInitialSearchResultSetHasBeenCreatedEvent;
use ApacheSolrForTypo3\Solr\Event\Search\AfterSearchHasBeenExecutedEvent;
use ApacheSolrForTypo3\Solr\Event\Search\AfterSearchQueryHasBeenPreparedEvent;
use ApacheSolrForTypo3\Solr\Search;
use ApacheSolrForTypo3\Solr\Search\QueryAware;
use ApacheSolrForTypo3\Solr\Search\SearchAware;
use ApacheSolrForTypo3\Solr\Search\SearchComponentManager;
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
use ApacheSolrForTypo3\Solr\System\Solr\Document\Document;
use ApacheSolrForTypo3\Solr\System\Solr\ResponseAdapter;
use ApacheSolrForTypo3\Solr\System\Solr\SolrIncompleteResponseException;
use Psr\EventDispatcher\EventDispatcherInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use UnexpectedValueException;

use function get_class;

/**
* The SearchResultSetService is responsible to build a SearchResultSet from a SearchRequest.
* It encapsulates the logic to trigger a search in order to be able to reuse it in multiple places.
Expand All @@ -64,18 +61,22 @@ class SearchResultSetService

protected QueryBuilder $queryBuilder;

protected EventDispatcherInterface $eventDispatcher;

public function __construct(
TypoScriptConfiguration $configuration,
Search $search,
?SolrLogManager $solrLogManager = null,
?SearchResultBuilder $resultBuilder = null,
?QueryBuilder $queryBuilder = null
?QueryBuilder $queryBuilder = null,
?EventDispatcherInterface $eventDispatcher = null
) {
$this->search = $search;
$this->typoScriptConfiguration = $configuration;
$this->logger = $solrLogManager ?? GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__);
$this->searchResultBuilder = $resultBuilder ?? GeneralUtility::makeInstance(SearchResultBuilder::class);
$this->queryBuilder = $queryBuilder ?? GeneralUtility::makeInstance(QueryBuilder::class, $configuration, $solrLogManager);
$this->eventDispatcher = $eventDispatcher ?? GeneralUtility::makeInstance(EventDispatcherInterface::class);
}

public function getIsSolrAvailable(bool $useCache = true): bool
Expand All @@ -94,26 +95,6 @@ public function getSearch(): Search
return $this->search;
}

protected function initializeRegisteredSearchComponents(Query $query, SearchRequest $searchRequest): void
{
$searchComponents = $this->getRegisteredSearchComponents();

foreach ($searchComponents as $searchComponent) {
/** @var Search\SearchComponent $searchComponent */
$searchComponent->setSearchConfiguration($this->typoScriptConfiguration->getSearchConfiguration());

if ($searchComponent instanceof QueryAware) {
$searchComponent->setQuery($query);
}

if ($searchComponent instanceof SearchRequestAware) {
$searchComponent->setSearchRequest($searchRequest);
}

$searchComponent->initializeSearchComponent();
}
}

protected function getResultSetClassName(): string
{
return $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultSetClassName '] ?? SearchResultSet::class;
Expand All @@ -129,7 +110,9 @@ public function search(SearchRequest $searchRequest): SearchResultSet
$resultSet = $this->getInitializedSearchResultSet($searchRequest);
$this->lastResultSet = $resultSet;

$resultSet = $this->handleSearchHook('beforeSearch', $resultSet);
$event = new AfterInitialSearchResultSetHasBeenCreatedEvent($resultSet, $searchRequest, $this->search, $this->typoScriptConfiguration);
$event = $this->eventDispatcher->dispatch($event);
$resultSet = $event->getSearchResultSet();
if ($this->shouldReturnEmptyResultSetWithoutExecutedSearch($searchRequest)) {
$resultSet->setHasSearched(false);
return $resultSet;
Expand All @@ -140,11 +123,13 @@ public function search(SearchRequest $searchRequest): SearchResultSet
$searchRequest->getResultsPerPage(),
$searchRequest->getAdditionalFilters()
);
$this->initializeRegisteredSearchComponents($query, $searchRequest);
$resultSet->setUsedQuery($query);

$event = new AfterSearchQueryHasBeenPreparedEvent($query, $searchRequest, $this->search, $this->typoScriptConfiguration);
$event = $this->eventDispatcher->dispatch($event);
$query = $event->getQuery();

$resultSet->setUsedQuery($query);
// performing the actual search, sending the query to the Solr server
$query = $this->modifyQuery($query, $searchRequest, $this->search);
$response = $this->doASearch($query, $searchRequest);

if ($searchRequest->getResultsPerPage() === 0) {
Expand All @@ -161,21 +146,21 @@ public function search(SearchRequest $searchRequest): SearchResultSet

$resultSet->setUsedAdditionalFilters($this->queryBuilder->getAdditionalFilters());

/** @var VariantsProcessor $variantsProcessor */
$variantsProcessor = GeneralUtility::makeInstance(
VariantsProcessor::class,
$this->typoScriptConfiguration,
$this->searchResultBuilder
);
$variantsProcessor->process($resultSet);

/** @var ResultSetReconstitutionProcessor $searchResultReconstitutionProcessor */
$searchResultReconstitutionProcessor = GeneralUtility::makeInstance(ResultSetReconstitutionProcessor::class);
$searchResultReconstitutionProcessor->process($resultSet);

$resultSet = $this->getAutoCorrection($resultSet);

return $this->handleSearchHook('afterSearch', $resultSet);
$event = new AfterSearchHasBeenExecutedEvent($resultSet, $query, $searchRequest, $this->search, $this->typoScriptConfiguration);
$event = $this->eventDispatcher->dispatch($event);
return $event->getSearchResultSet();
}

/**
Expand Down Expand Up @@ -303,46 +288,6 @@ protected function performAutoCorrection(SearchResultSet $searchResultSet): Sear
return $searchResultSet;
}

/**
* Allows to modify a query before eventually handing it over to Solr.
*
* @param Query $query The current query before it's being handed over to Solr.
* @param SearchRequest $searchRequest The searchRequest, relevant in the current context
* @param Search $search The search, relevant in the current context
*
* @return Query The modified query that is actually going to be given to Solr.
*
* @throws UnexpectedValueException
*/
protected function modifyQuery(Query $query, SearchRequest $searchRequest, Search $search): Query
{
// hook to modify the search query
if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifySearchQuery'] ?? null)) {
foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifySearchQuery'] as $classReference) {
$queryModifier = GeneralUtility::makeInstance($classReference);

if ($queryModifier instanceof Modifier) {
if ($queryModifier instanceof SearchAware) {
$queryModifier->setSearch($search);
}

if ($queryModifier instanceof SearchRequestAware) {
$queryModifier->setSearchRequest($searchRequest);
}

$query = $queryModifier->modifyQuery($query);
} else {
throw new UnexpectedValueException(
get_class($queryModifier) . ' must implement interface ' . Modifier::class,
1310387414
);
}
}
}

return $query;
}

/**
* Retrieves a single document from solr by document id.
*/
Expand All @@ -362,25 +307,6 @@ public function getDocumentById(string $documentId): SearchResult
return $this->searchResultBuilder->fromApacheSolrDocument($resultDocument);
}

/**
* This method is used to call the registered hooks during the search execution.
*/
private function handleSearchHook(string $eventName, SearchResultSet $resultSet): SearchResultSet
{
if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr'][$eventName] ?? null)) {
return $resultSet;
}

foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr'][$eventName] as $classReference) {
$afterSearchProcessor = GeneralUtility::makeInstance($classReference);
if ($afterSearchProcessor instanceof SearchResultSetProcessor) {
$afterSearchProcessor->process($resultSet);
}
}

return $resultSet;
}

public function getLastResultSet(): ?SearchResultSet
{
return $this->lastResultSet;
Expand All @@ -404,12 +330,4 @@ protected function getInitialSearchIsConfigured(): bool
{
return $this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchShowResultsOfInitialEmptyQuery() || $this->typoScriptConfiguration->getSearchInitializeWithQuery() || $this->typoScriptConfiguration->getSearchShowResultsOfInitialQuery();
}

/**
* Returns registered search components
*/
protected function getRegisteredSearchComponents(): array
{
return GeneralUtility::makeInstance(SearchComponentManager::class)->getSearchComponents();
}
}
29 changes: 0 additions & 29 deletions Classes/Domain/Search/SearchRequestAware.php

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query;
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResultSet;
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResultSetProcessor;
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
use ApacheSolrForTypo3\Solr\HtmlContentExtractor;
use Doctrine\DBAL\Exception as DBALException;
Expand All @@ -34,7 +33,7 @@
* @author Dimitri Ebert <dimitri.ebert@dkd.de>
* @author Timo Hund <timo.hund@dkd.de>
*/
class StatisticsWriterProcessor implements SearchResultSetProcessor
class StatisticsWriterProcessor
{
protected StatisticsRepository $statisticsRepository;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?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 ApacheSolrForTypo3\Solr\Event\Search;

use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResultSet;
use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest;
use ApacheSolrForTypo3\Solr\Search;
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;

/**
* Event which is used when the searchResultSet has been created and by the SearchQuery.
*
* Previously used via
* $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['beforeSearch']
*/
final class AfterInitialSearchResultSetHasBeenCreatedEvent
{
public function __construct(
private SearchResultSet $searchResultSet,
private readonly SearchRequest $searchRequest,
private readonly Search $search,
private readonly TypoScriptConfiguration $typoScriptConfiguration
) {
}

public function getSearchResultSet(): SearchResultSet
{
return $this->searchResultSet;
}

public function setSearchResultSet(SearchResultSet $searchResultSet): void
{
$this->searchResultSet = $searchResultSet;
}

public function getSearchRequest(): SearchRequest
{
return $this->searchRequest;
}

public function getSearch(): Search
{
return $this->search;
}

public function getTypoScriptConfiguration(): TypoScriptConfiguration
{
return $this->typoScriptConfiguration;
}
}
Loading

0 comments on commit 71fe543

Please sign in to comment.