diff --git a/bundle/QueryType/SearchQueryType.php b/bundle/QueryType/SearchQueryType.php index 08e82590..c7cc002f 100644 --- a/bundle/QueryType/SearchQueryType.php +++ b/bundle/QueryType/SearchQueryType.php @@ -7,18 +7,25 @@ use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Ibexa\Core\QueryType\OptionsResolverBasedQueryType; use Netgen\Bundle\SiteBundle\API\Search\Criterion\FullText; use Netgen\IbexaSiteApi\API\Site; use Symfony\Component\OptionsResolver\OptionsResolver; +use function class_exists; use function count; +use function in_array; +use function is_a; use function trim; final class SearchQueryType extends OptionsResolverBasedQueryType { - public function __construct(private Site $site, private ConfigResolverInterface $configResolver) {} + public function __construct( + private readonly Site $site, + private readonly ConfigResolverInterface $configResolver, + ) {} public static function getName(): string { @@ -27,10 +34,11 @@ public static function getName(): string protected function configureOptions(OptionsResolver $optionsResolver): void { - $optionsResolver->setRequired(['search_text', 'content_types', 'subtree']); + $optionsResolver->setRequired(['search_text', 'content_types', 'subtree', 'sort', 'order']); $optionsResolver->setAllowedTypes('search_text', 'string'); $optionsResolver->setAllowedTypes('content_types', 'string[]'); + $optionsResolver->setAllowedTypes('sort', 'string[]'); $optionsResolver->setAllowedTypes('subtree', ['int', 'string']); $optionsResolver->setAllowedValues( @@ -38,8 +46,20 @@ protected function configureOptions(OptionsResolver $optionsResolver): void static fn (string $searchText): bool => trim($searchText) !== '', ); + $optionsResolver->setAllowedValues( + 'sort', + fn (array $classNames): bool => $this->validateSortConfig($classNames), + ); + + $optionsResolver->setAllowedValues( + 'order', + static fn (string $order): bool => in_array($order, [Query::SORT_ASC, Query::SORT_DESC], true), + ); + $optionsResolver->setDefault('content_types', $this->configResolver->getParameter('search.content_types', 'ngsite')); $optionsResolver->setDefault('subtree', $this->site->getSettings()->rootLocationId); + $optionsResolver->setDefault('sort', [SortClause\DatePublished::class]); + $optionsResolver->setDefault('order', Query::SORT_DESC); } protected function doGetQuery(array $parameters): Query @@ -59,6 +79,29 @@ protected function doGetQuery(array $parameters): Query $query->query = new FullText(trim($parameters['search_text'])); $query->filter = new Criterion\LogicalAnd($criteria); + $sortClauses = []; + + /** @var class-string<\Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause> $className */ + foreach ($parameters['sort'] as $className) { + $sortClauses[] = new $className($parameters['order']); + } + + $query->sortClauses = $sortClauses; + return $query; } + + /** + * @param string[] $classNames + */ + private function validateSortConfig(array $classNames): bool + { + foreach ($classNames as $className) { + if (!class_exists($className) || !is_a($className, SortClause::class, true)) { + return false; + } + } + + return true; + } }