diff --git a/src/Search/Algolia/Index.php b/src/Search/Algolia/Index.php index 6eab453935b..e4e4e37d148 100644 --- a/src/Search/Algolia/Index.php +++ b/src/Search/Algolia/Index.php @@ -77,16 +77,20 @@ public function update() return $this; } - public function searchUsingApi($query, $fields = null) + public function searchUsingApi($query, $fields = null, $arguments = []) { - $arguments = ['query' => $query]; + $params = ['query' => $query]; if ($fields) { - $arguments['restrictSearchableAttributes'] = implode(',', Arr::wrap($fields)); + $params['restrictSearchableAttributes'] = implode(',', Arr::wrap($fields)); + } + + foreach ($arguments as $key => $value) { + $params[Str::camel($key)] = $value; } try { - $response = $this->client()->searchSingleIndex($this->name, $arguments); + $response = $this->client()->searchSingleIndex($this->name, $params); } catch (AlgoliaException $e) { $this->handleAlgoliaException($e); } diff --git a/src/Search/Algolia/Query.php b/src/Search/Algolia/Query.php index 4231734311b..74135f7782d 100644 --- a/src/Search/Algolia/Query.php +++ b/src/Search/Algolia/Query.php @@ -11,7 +11,7 @@ public function getSearchResults($query) { $results = Blink::once( "search-algolia-{$this->index->name()}-".md5($query), - fn () => $this->index->searchUsingApi($query) + fn () => $this->index->searchUsingApi($query, arguments: $this->arguments()) ); return $results->map(function ($result, $i) use ($results) { diff --git a/src/Search/QueryBuilder.php b/src/Search/QueryBuilder.php index fc01ad19f68..42e68e6045f 100644 --- a/src/Search/QueryBuilder.php +++ b/src/Search/QueryBuilder.php @@ -16,12 +16,18 @@ abstract class QueryBuilder extends BaseQueryBuilder protected $query; protected $index; protected $withData = true; + protected $arguments = []; public function __construct(Index $index) { $this->index = $index; } + public function arguments(): array + { + return $this->arguments; + } + public function query($query) { $this->query = $query; @@ -43,6 +49,13 @@ public function withoutData() return $this; } + public function withArguments(array $arguments) + { + $this->arguments = $arguments; + + return $this; + } + public function get($columns = ['*']) { return $this->withFakeQueryLogging(fn () => parent::get($columns)); diff --git a/src/Search/Tags.php b/src/Search/Tags.php index 909ba146418..3c16b97aefe 100644 --- a/src/Search/Tags.php +++ b/src/Search/Tags.php @@ -2,6 +2,7 @@ namespace Statamic\Search; +use Illuminate\Support\Str; use Statamic\Facades\Search; use Statamic\Facades\Site; use Statamic\Tags\Concerns; @@ -37,6 +38,7 @@ public function results() $this->queryConditions($builder); $this->queryScopes($builder); $this->queryOrderBys($builder); + $this->appendArguments($builder); $results = $this->getQueryResults($builder); @@ -62,4 +64,15 @@ protected function querySite($query) return $query->whereIn('site', $sites); } + + protected function appendArguments($query) + { + $arguments = collect($this->params) + ->filter(fn ($value, $key) => Str::startsWith($key, 'with:')) + ->mapWithKeys(fn ($value, $key) => [Str::after($key, 'with:') => $value]); + + return $arguments->count() + ? $query->withArguments($arguments->all()) + : $query; + } } diff --git a/tests/Search/AlgoliaQueryTest.php b/tests/Search/AlgoliaQueryTest.php index 0696da4235c..052dc0a413b 100644 --- a/tests/Search/AlgoliaQueryTest.php +++ b/tests/Search/AlgoliaQueryTest.php @@ -15,7 +15,7 @@ public function it_adds_scores() { $index = Mockery::mock(Index::class); $index->shouldReceive('name'); - $index->shouldReceive('searchUsingApi')->with('foo')->once()->andReturn(collect([ + $index->shouldReceive('searchUsingApi')->with('foo', null, [])->once()->andReturn(collect([ ['reference' => 'a'], ['reference' => 'b'], ['reference' => 'c'], diff --git a/tests/Tags/SearchTest.php b/tests/Tags/SearchTest.php index 15c316bb41b..dd5ae9cf452 100644 --- a/tests/Tags/SearchTest.php +++ b/tests/Tags/SearchTest.php @@ -62,4 +62,29 @@ public function it_outputs_results_using_alias() ) ); } + + #[Test] + public function it_passes_along_arguments() + { + $entryA = EntryFactory::id('a')->collection('test')->data(['title' => 'entry a'])->create(); + $entryB = EntryFactory::id('b')->collection('test')->data(['title' => 'entry b'])->create(); + + $builder = $this->mock(QueryBuilder::class)->makePartial(); + $builder->shouldReceive('ensureExists', 'search', 'withData', 'limit', 'offset', 'where')->andReturnSelf(); + $builder->shouldReceive('get')->andReturn(collect([$entryA, $entryB])); + + Search::shouldReceive('index')->with(null)->once()->andReturn($builder); + + $this->get('/whatever?q=foo'); // just a way to get a query param into the request(). the url is irrelevant. + + $this->assertEquals( + '', + $this->tag( + '{{ search:results :with:filters="get:filters" :with:sort="get:sort" }}<{{ title }}>{{ /search:results }}', + ['get' => ['q' => 'foo', 'filters' => ['category' => 'A'], 'sort' => 'distance:asc']] + ) + ); + + $this->assertEquals(['filters' => ['category' => 'A'], 'sort' => 'distance:asc'], $builder->arguments()); + } }