diff --git a/Classes/Backend/AbstractBackend.php b/Classes/Backend/AbstractBackend.php index aff993b..68e3eeb 100644 --- a/Classes/Backend/AbstractBackend.php +++ b/Classes/Backend/AbstractBackend.php @@ -33,6 +33,17 @@ public abstract function initialize(); */ public abstract function search(string $searchString, int $currentPage, $category = null): array; + /** + * Return an array of words for search suggestions + * + * @param string $searchString + * @param int $maxItems + * @param int $languageUid + * + * @return array + */ + public abstract function suggest(string $searchString, int $maxItems, int $languageUid): array; + /** * Takes the raw result row and converts it into a standardized format to be used in the output. * diff --git a/Classes/Backend/IndexedSearchBackend.php b/Classes/Backend/IndexedSearchBackend.php index 5959b41..45bafe0 100644 --- a/Classes/Backend/IndexedSearchBackend.php +++ b/Classes/Backend/IndexedSearchBackend.php @@ -4,6 +4,7 @@ use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Context\Context; +use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; use TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository; @@ -84,6 +85,29 @@ function ($item) { return $data; } + public function suggest(string $searchString, int $maxItems, int $languageUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('index_words'); + $queryBuilder + ->select('w.baseword') + ->from('index_words', 'w') + ->join('w', 'index_rel', 'r', 'w.wid=r.wid') + ->join('r', 'index_phash', 'p', 'r.phash=p.phash') + ->where( + $queryBuilder->expr()->like('w.baseword', $queryBuilder->createNamedParameter($searchString . '%')), + $queryBuilder->expr()->eq('p.sys_language_uid', $queryBuilder->createNamedParameter($languageUid, \PDO::PARAM_INT)) + ) + ->groupBy('w.baseword') + ->setMaxResults($maxItems) + ->orderBy('w.baseword', 'asc'); + + $result = $queryBuilder->executeQuery(); + $words = $result->fetchFirstColumn(); + $result->free(); + + return $words; + } + protected function initializeSearchRepository($searchData, int $itemsPerPage) { $searchData['languageUid'] = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'id', 0); diff --git a/Classes/Middleware/SuggestMiddleware.php b/Classes/Middleware/SuggestMiddleware.php new file mode 100644 index 0000000..62785b7 --- /dev/null +++ b/Classes/Middleware/SuggestMiddleware.php @@ -0,0 +1,52 @@ +getUri()->getPath() === '/search-auto-complete') { + $searchString = $request->getQueryParams()['q']; + try { + $minCharacters = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('versatile_search', 'suggest.minCharacters') ?? 3; + } catch (ExtensionConfigurationPathDoesNotExistException $exception) { + $minCharacters = 3; + } + + if (empty($searchString) || mb_strlen($searchString) < $minCharacters) { + return new JsonResponse([]); + } + + /** @var AbstractBackend $backend */ + $backend = GeneralUtility::makeInstance(BackendUtility::getSearchBackend(), []); + try { + $maxItems = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('versatile_search', 'suggest.maxItems') ?? 10; + } catch (ExtensionConfigurationPathDoesNotExistException $exception) { + $maxItems = 10; + } + /** @var SiteLanguage $language */ + $language = $request->getAttribute('language'); + + $suggestions = $backend->suggest($searchString, $maxItems, $language->getLanguageId()); + + return new JsonResponse($suggestions); + } + + return $handler->handle($request); + } +} diff --git a/Configuration/RequestMiddlewares.php b/Configuration/RequestMiddlewares.php new file mode 100644 index 0000000..ff89314 --- /dev/null +++ b/Configuration/RequestMiddlewares.php @@ -0,0 +1,15 @@ + [ + 'versatile-search/suggest' => [ + 'target' => \WEBcoast\VersatileSearch\Middleware\SuggestMiddleware::class, + 'after' => [ + 'typo3/cms-frontend/maintenance-mode' + ], + 'before' => [ + 'typo3/cms-frontend/backend-user-authentication' + ] + ] + ] +]; diff --git a/ext_conf_template.txt b/ext_conf_template.txt index 31fa59f..e45643b 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -1,2 +1,5 @@ # cat=General; type=options[LLL:EXT:versatile_search/Resources/Private/Language/backend.xlf:conf.search.backend.indexed_search=indexed_search,LLL:EXT:versatile_search/Resources/Private/Language/backend.xlf:conf.search.backend.none=none]; label=LLL:EXT:versatile_search/Resources/Private/Language/backend.xlf:conf.search.backend search.backend = indexed_search + +suggest.maxItems = 10 +suggest.minCharacters = 3