Skip to content

Commit aa0df8b

Browse files
committed
feature: Global search resource opt-in
1 parent b60dbfe commit aa0df8b

4 files changed

Lines changed: 102 additions & 1 deletion

File tree

docs/03-resources/10-global-search.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,29 @@ public function panel(Panel $panel): Panel
172172
}
173173
```
174174

175+
## Requiring resources to opt in to global search
176+
177+
By default, all resources with a [title attribute](#setting-global-search-result-titles) are included in global search results. If you'd prefer resources to explicitly opt in, you can use the `globalSearchResourceOptIn()` method in the [configuration](../panel-configuration):
178+
179+
```php
180+
use Filament\Panel;
181+
182+
public function panel(Panel $panel): Panel
183+
{
184+
return $panel
185+
// ...
186+
->globalSearchResourceOptIn();
187+
}
188+
```
189+
190+
Now, only resources that explicitly set `$isGloballySearchable` to `true` will be included in global search results:
191+
192+
```php
193+
protected static bool $isGloballySearchable = true;
194+
```
195+
196+
Resources that do not declare this property will be excluded from global search, even if they have a title attribute set.
197+
175198
## Registering global search key bindings
176199

177200
The global search field can be opened using keyboard shortcuts. To configure these, pass the `globalSearchKeyBindings()` method to the [configuration](../panel-configuration):

packages/panels/src/Panel/Concerns/HasGlobalSearch.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ trait HasGlobalSearch
2626

2727
protected string | Closure | null $globalSearchFieldSuffix = null;
2828

29+
protected bool $isGlobalSearchResourceOptIn = false;
30+
2931
public function globalSearch(string | bool $provider = true, GlobalSearchPosition | Closure | null $position = null): static
3032
{
3133
if (is_string($provider) && (! in_array(GlobalSearchProvider::class, class_implements($provider)))) {
@@ -121,6 +123,18 @@ public function getGlobalSearchFieldSuffix(): ?string
121123
return $this->evaluate($this->globalSearchFieldSuffix);
122124
}
123125

126+
public function globalSearchResourceOptIn(bool $condition = true): static
127+
{
128+
$this->isGlobalSearchResourceOptIn = $condition;
129+
130+
return $this;
131+
}
132+
133+
public function isGlobalSearchResourceOptIn(): bool
134+
{
135+
return $this->isGlobalSearchResourceOptIn;
136+
}
137+
124138
public function getGlobalSearchProvider(): ?GlobalSearchProvider
125139
{
126140
$provider = $this->globalSearchProvider;

packages/panels/src/Resources/Resource/Concerns/HasGlobalSearch.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
namespace Filament\Resources\Resource\Concerns;
44

55
use Filament\Actions\Action;
6+
use Filament\Facades\Filament;
67
use Filament\GlobalSearch\GlobalSearchResult;
78
use Illuminate\Contracts\Support\Htmlable;
89
use Illuminate\Database\Connection;
910
use Illuminate\Database\Eloquent\Builder;
1011
use Illuminate\Database\Eloquent\Model;
1112
use Illuminate\Support\Arr;
1213
use Illuminate\Support\Collection;
14+
use ReflectionProperty;
1315

1416
use function Filament\Support\generate_search_column_expression;
1517
use function Filament\Support\generate_search_term_expression;
@@ -31,7 +33,18 @@ trait HasGlobalSearch
3133

3234
public static function canGloballySearch(): bool
3335
{
34-
return static::$isGloballySearchable && count(static::getGloballySearchableAttributes()) && static::canAccess();
36+
$isGloballySearchable = static::$isGloballySearchable;
37+
38+
if (
39+
$isGloballySearchable &&
40+
Filament::getCurrentOrDefaultPanel()?->isGlobalSearchResourceOptIn()
41+
) {
42+
$isGloballySearchable = (new ReflectionProperty(static::class, 'isGloballySearchable'))
43+
->getDeclaringClass()
44+
->getName() === static::class;
45+
}
46+
47+
return $isGloballySearchable && count(static::getGloballySearchableAttributes()) && static::canAccess();
3548
}
3649

3750
/**

tests/src/Panels/GlobalSearch/GlobalSearchTest.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
use Filament\GlobalSearch\GlobalSearchResults;
66
use Filament\GlobalSearch\Providers\Contracts\GlobalSearchProvider;
77
use Filament\Livewire\GlobalSearch;
8+
use Filament\Resources\Resource;
89
use Filament\Tests\Fixtures\Models\Post;
910
use Filament\Tests\Fixtures\Models\User;
11+
use Filament\Tests\Fixtures\Resources\Posts\PostResource;
12+
use Filament\Tests\Fixtures\Resources\Users\UserResource;
1013
use Filament\Tests\Panels\GlobalSearch\TestCase;
1114
use Illuminate\Database\Eloquent\Factories\Sequence;
1215
use Illuminate\Support\Str;
@@ -78,6 +81,54 @@
7881
expect($categories[1])->toBe('posts');
7982
});
8083

84+
it('excludes resources without explicit `$isGloballySearchable` when `globalSearchResourceOptIn()` is enabled', function (): void {
85+
Filament::getCurrentOrDefaultPanel()->globalSearchResourceOptIn();
86+
87+
expect(PostResource::canGloballySearch())->toBeFalse();
88+
expect(UserResource::canGloballySearch())->toBeFalse();
89+
});
90+
91+
it('includes resources with explicit `$isGloballySearchable` when `globalSearchResourceOptIn()` is enabled', function (): void {
92+
Filament::getCurrentOrDefaultPanel()->globalSearchResourceOptIn();
93+
94+
expect(OptedInGlobalSearchResource::canGloballySearch())->toBeTrue();
95+
});
96+
97+
it('includes all resources by default without `globalSearchResourceOptIn()`', function (): void {
98+
expect(PostResource::canGloballySearch())->toBeTrue();
99+
expect(UserResource::canGloballySearch())->toBeTrue();
100+
expect(OptedInGlobalSearchResource::canGloballySearch())->toBeTrue();
101+
});
102+
103+
it('does not return search results for resources without explicit `$isGloballySearchable` when `globalSearchResourceOptIn()` is enabled', function (): void {
104+
Filament::getCurrentOrDefaultPanel()->globalSearchResourceOptIn();
105+
106+
$post = Post::factory()->create();
107+
108+
livewire(GlobalSearch::class)
109+
->set('search', $post->title)
110+
->assertDontSee($post->title);
111+
});
112+
113+
class OptedInGlobalSearchResource extends Resource
114+
{
115+
protected static ?string $model = Post::class;
116+
117+
protected static ?string $recordTitleAttribute = 'title';
118+
119+
protected static bool $isGloballySearchable = true;
120+
121+
protected static ?string $slug = 'opted-in-global-search-posts';
122+
123+
/**
124+
* @return array<string>
125+
*/
126+
public static function getGloballySearchableAttributes(): array
127+
{
128+
return ['title'];
129+
}
130+
}
131+
81132
class CustomSearchProvider implements GlobalSearchProvider
82133
{
83134
public function getResults(string $query): ?GlobalSearchResults

0 commit comments

Comments
 (0)