howdu/filament-record-switcher
Installation
composer require howdu/filament-record-switcher
Publish the config (if needed):
php artisan vendor:publish --provider="Howdu\FilamentRecordSwitcher\FilamentRecordSwitcherServiceProvider"
Enable for a Resource Add the trait to your Filament resource class:
use Howdu\FilamentRecordSwitcher\Concerns\HasRecordSwitcher;
class PostResource extends Resource
{
use HasRecordSwitcher;
// ...
}
First Use Case
Resource Integration
HasRecordSwitcher to any Filament resource.$recordSwitcher property in the resource class:
protected static ?string $recordSwitcher = 'custom'; // or 'default'
Search & Navigation
getRecordSwitcherQuery() to modify the query:
public function getRecordSwitcherQuery(): Builder
{
return parent::getRecordSwitcherQuery()
->where('is_active', true);
}
Styling & UX
$recordSwitcherIcon to change the dropdown icon:
protected static string $recordSwitcherIcon = 'heroicon-o-chevron-down';
canAccessRecordSwitcher():
public static function canAccessRecordSwitcher(): bool
{
return auth()->user()->can('switch_records');
}
Multi-Resource Projects
$recordSwitcherGroup:
protected static ?string $recordSwitcherGroup = 'Admin';
canAccessRecordSwitcher() to gate access per record/user.getRecordSwitcherActions():
public function getRecordSwitcherActions(): array
{
return [
Action::make('archive')
->label('Archive Record')
->action(fn (Record $record) => $record->update(['is_active' => false])),
];
}
getRecordSwitcherQuery():
return parent::getRecordSwitcherQuery()->paginate(20);
Query Performance
->select('id', 'title', 'slug') to limit columns or use ->with(['relationship']) sparingly.return Cache::remember("record-switcher-{$this->getModel()}", now()->addHours(1), fn() => parent::getRecordSwitcherQuery());
Permission Conflicts
canAccessRecordSwitcher() or filter the query:
public function getRecordSwitcherQuery(): Builder
{
return parent::getRecordSwitcherQuery()->whereIn('id', fn($q) => $q->select('id')->from('posts')->where('user_id', auth()->id()));
}
Styling Overrides
$recordSwitcherClass property to add custom classes:
protected static string $recordSwitcherClass = 'filament-record-switcher-custom';
Relationships & Eager Loading
getRecordSwitcherQuery():
return parent::getRecordSwitcherQuery()->with(['author', 'category']);
->toSql() to getRecordSwitcherQuery() to debug:
\Log::info($this->getRecordSwitcherQuery()->toSql());
dd($this->canAccessRecordSwitcher()) to verify access logic.php artisan optimize:clear
Custom Dropdown Content
getRecordSwitcherDropdownItems() to replace the default list:
public function getRecordSwitcherDropdownItems(): array
{
return [
DropdownItem::make('Custom Item')
->url(fn () => route('custom.path'))
->icon('heroicon-o-external-link'),
];
}
Event Listeners
record-switcher.before-switch to intercept navigation:
event(new RecordSwitcherEvent($record));
php artisan vendor:publish --tag=filament-record-switcher-config
Testing
$this->getFilament()->actingAs($user)
->get($resource->getUrl())
->assertSeeInDropdown($record->title);
assertSoftDeletes() for soft-deleted records if applicable.public function getRecordSwitcherActions(): array
{
return [
Action::make('edit')
->label('Quick Edit')
->icon('heroicon-o-pencil')
->url(fn (Record $record) => $this->getUrl('edit', ['record' => $record])),
];
}
protected static ?string $recordSwitcherLabel = __('filament-record-switcher::labels.switch_record');
```markdown
### Config Quirks
- **Default vs. Custom**: The `default` switcher uses Filament’s global search styling, while `custom` provides a blank slate for styling.
- **Grouping**: Groups are alphabetically sorted by default. Override `getRecordSwitcherGroupSort()` to customize:
```php
public static function getRecordSwitcherGroupSort(): array
{
return ['Admin', 'Content', 'Settings'];
}
protected static ?string $recordSwitcherEmptyState = __('No records found.');
How can I help you explore Laravel packages today?