Skip to content

Commit

Permalink
added ScoutSelect component, mofidied plugin structure
Browse files Browse the repository at this point in the history
  • Loading branch information
kainiklas committed Jan 16, 2024
1 parent 31a5828 commit 64e08dd
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 22 deletions.
85 changes: 65 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,68 +25,113 @@ composer require kainiklas/filament-scout

### Enable Table Search for Resources

To use Scout Search instead of the default search, add the trait `InteractsWithScout` to any Page which contains a table, e.g. `app\Filament\Resources\MyResource\Pages\ListMyResources.php`:
To use Scout Search instead of the default search on a table, add the trait `InteractsWithScout` to any Page which contains a table, e.g. `app\Filament\Resources\MyResource\Pages\ListMyResources.php`:

```php
use Kainiklas\FilamentScout\Traits\InteractsWithScout;

class ListMyResources extends ListRecords
{
use InteractsWithScout;
}
```

The table defined in the resource needs to be `searchable()` as described in the [Filament table docs](https://filamentphp.com/docs/3.x/tables/advanced#searching-records-with-laravel-scout). Making each column searchable is not required anymore, as the content of what is searchable is defined within scout.

### Enable Global Search

1. Check how to enable [Global Search in the Filament Documentation](https://filamentphp.com/docs/3.x/panels/resources/global-search).
1. Set a `$recordTitleAttribute` on your resource: [Setting global search result title](https://filamentphp.com/docs/3.x/panels/resources/global-search#setting-global-search-result-titles).
2. (Optional) Add details by implementing the method `getGlobalSearchResultDetails(Model $record)` in your Resource: [Adding extra details to global search results](https://filamentphp.com/docs/3.x/panels/resources/global-search#adding-extra-details-to-global-search-results).

protected static string $resource = MyResource::class;
```php
class ContractResource extends Resource
{
// required to enable global search
protected static ?string $recordTitleAttribute = 'name';

protected function getHeaderActions(): array
// optional: details
public static function getGlobalSearchResultDetails(Model $record): array
{
return [
Actions\CreateAction::make(),
'Category' => $record->category->name,
];
}
}
```

2. Add the Plugin `FilamentScoutPlugin` to your panel configuration, e.g., in `app\Providers\Filament\AdminPanelProvider.php`.

```php
use Kainiklas\FilamentScout\FilamentScoutPlugin;

class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
$plugins([
FilamentScoutPlugin::make()
]);
}
}
```

### Enable Global Search
### Meilisearch

1. Check how to enable [Global Search in the Filament Documentation](https://filamentphp.com/docs/3.x/panels/resources/global-search). It is sufficient to add `searchable()` to the table as described [here](https://filamentphp.com/docs/3.x/tables/advanced#searching-records-with-laravel-scout).
2. (Optional) Add Details by implementing the method `getGlobalSearchResultDetails(Model $record)` in your Resource File as described [here](https://filamentphp.com/docs/3.x/panels/resources/global-search#adding-extra-details-to-global-search-results).
3. Add the GlobalSearchProvider `ScoutGlobalSearchProvider` to your panel configuration, e.g., in `app\Providers\Filament\AdminPanelProvider.php`.
If you are using [Meilisearch](https://www.meilisearch.com/), you can activate meilisearch specific features (search context highlighting):

Example:
1. Configure the plugin.

```php
use Kainiklas\FilamentScout\Providers\ScoutGlobalSearchProvider;
use Kainiklas\FilamentScout\FilamentScoutPlugin;

class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
...
$panel->globalSearch(ScoutGlobalSearchProvider::class);
...
$plugins([
FilamentScoutPlugin::make()
->useMeiliSearch() // enables meilisearch specific features
]);
}
}
```

### Meilisearch (BETA)

If you are using [Meilisearch](https://www.meilisearch.com/), you may want to try the GlobalSearchProvider `MeilisearchGlobalSearchProvider` which supports context highlighting.

If you implement `getGlobalSearchResultDetails()` you need to adapt this in the following way:
2. (Optional) Implement/ Adapt `getGlobalSearchResultDetails()` in your Resource:

```php
public static function getGlobalSearchResultDetails(Model $record): array
{
// change the filament default implementation from this
// return [
// 'AttributeTitle' => $record->attribute_name
// ];


// to this
return [
'attribute_name' => "AttributeTitle"
'scout_attribute_name' => "AttributeTitle"
];
}
```

### Search in Select Form Field

To enable scout search in your select form fields use the provided `ScoutSelect` component:

```php
use Kainiklas\FilamentScout\Forms\Components\ScoutSelect;

ScoutSelect::make('company_id')
->searchable()
->relationship('company', 'name')
```

Technically, the `ScoutSelect` component inherits from `Filament\Forms\Components\Select`. It changes the `relation()` method such that it overrides the `getSearchResultsUsing()` method to use scout.

*Hint*: Only values which are accessible and defined by scout are searchable.

## Testing

```bash
Expand Down
47 changes: 47 additions & 0 deletions src/FilamentScoutPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Kainiklas\FilamentScout;

use Filament\Contracts\Plugin;
use Filament\Panel;
use Kainiklas\FilamentScout\Providers\MeilisearchGlobalSearchProvider;
use Kainiklas\FilamentScout\Providers\ScoutGlobalSearchProvider;
use Kainiklas\FilamentScout\Traits\ConfigurePlugin;

class FilamentScoutPlugin implements Plugin
{
use ConfigurePlugin;

public function getId(): string
{
return 'kainiklas-filament-scout-plugin';
}

public function register(Panel $panel): void
{
//
}

public function boot(Panel $panel): void
{
// Global Search Provider
if ($this->getUseMeiliSearch()) {
$panel->globalSearch(MeilisearchGlobalSearchProvider::class);
} else {
$panel->globalSearch(ScoutGlobalSearchProvider::class);
}
}

public static function make(): static
{
return app(static::class);
}

public static function get(): static
{
/** @var static $plugin */
$plugin = filament(app(static::class)->getId());

return $plugin;
}
}
34 changes: 34 additions & 0 deletions src/Forms/Components/ScoutSelect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Kainiklas\FilamentScout\Forms\Components;

use Closure;
use Filament\Forms\Components\Select as FilamentSelect;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Str;

class ScoutSelect extends FilamentSelect
{
public function relationship(string | Closure | null $name = null, string | Closure | null $titleAttribute = null, ?Closure $modifyQueryUsing = null): static
{
parent::relationship($name, $titleAttribute, $modifyQueryUsing);

$this->getSearchResultsUsing(function (ScoutSelect $component, string $search): array {
$relationship = Relation::noConstraints(fn () => $component->getRelationship());
$qualifiedRelatedKeyName = $component->getQualifiedRelatedKeyNameForRelationship($relationship);

return $relationship
->getRelated()
->search($search, function ($scout, string $query, array $options) {
$options['limit'] = $this->getOptionsLimit();

return $scout->search($query, $options);
})
->get()
->pluck($component->getRelationshipTitleAttribute(), Str::afterLast($qualifiedRelatedKeyName, '.'))
->toArray();
});

return $this;
}
}
3 changes: 1 addition & 2 deletions src/Providers/MeilisearchGlobalSearchProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Filament\GlobalSearch\Contracts\GlobalSearchProvider;
use Filament\GlobalSearch\GlobalSearchResult;
use Filament\GlobalSearch\GlobalSearchResults;
use Filament\Resources\Resource;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\HtmlString;

Expand All @@ -18,7 +17,7 @@ public function getResults(string $query): ?GlobalSearchResults
$builder = GlobalSearchResults::make();

foreach (Filament::getResources() as $resource) {
/** @var resource $resource * */
/** @var Filament\Resources\Resource $resource * */
if (! $resource::canGloballySearch()) {

Check failure on line 21 in src/Providers/MeilisearchGlobalSearchProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to static method canGloballySearch() on an unknown class Filament\Facades\Filament\Resources\Resource.

Check failure on line 21 in src/Providers/MeilisearchGlobalSearchProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

PHPDoc tag @var for variable $resource contains unknown class Filament\Facades\Filament\Resources\Resource.
continue;
}
Expand Down
24 changes: 24 additions & 0 deletions src/Traits/ConfigurePlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Kainiklas\FilamentScout\Traits;

trait ConfigurePlugin
{
// use EvaluatesClosures;

protected bool $useMeiliSearch = false;

// protected bool $useScoutInSelect = false;

public function useMeiliSearch(bool $condition = true): static
{
$this->useMeiliSearch = $condition;

return $this;
}

public function getUseMeiliSearch(): bool
{
return $this->useMeiliSearch;
}
}

0 comments on commit 64e08dd

Please sign in to comment.