kirschbaum-development/livewire-filters
Installation:
composer require kirschbaum-development/livewire-filters
php artisan vendor:publish --tag=livewire-filters-views
(Publish views to customize styling; config is optional unless using query strings.)
First Use Case:
Filter a Livewire component's data in real-time. For example, in a UserList component:
use KirschbaumDevelopment\LivewireFilters\Filters;
public $filters = [];
public function mount()
{
Filters::add('search', 'text', fn($query) => $query->where('name', 'like', '%'.$this->filters['search'].'%'));
}
public function render()
{
return view('livewire.user-list', [
'users' => User::query()->apply($this->filters)->get(),
]);
}
Add Filter UI:
<livewire:user-list />
<livewire:filters wire:model="filters" />
app/Http/Livewire/Filters.php (if published) for config (e.g., query string sync).resources/views/vendor/livewire-filters/) to customize UI.Define Filters:
Use Filters::add() in mount() to register filters with:
'search', 'status').'text', 'select', 'date', etc.).fn($query) => $query->where(...)).public function mount()
{
Filters::add('status', 'select', fn($query) => $query->where('status', $this->filters['status'] ?? null))
->options([
'active' => 'Active',
'inactive' => 'Inactive',
]);
}
Bind to Livewire Model:
Use wire:model="filters" in the <livewire:filters /> component to sync input with the parent component’s $filters property.
Apply Filters:
Chain apply($this->filters) to your query in render():
User::query()->apply($this->filters)->get();
Dynamic Filter Options:
Fetch options from a database or API in updatedFilter hooks:
public function updatedFilterStatus()
{
$this->filters['status_options'] = Status::all()->pluck('name', 'id');
}
Multi-Component Filters: Share filters across components by extending a base class or using a trait:
trait UsesSharedFilters
{
public function mount()
{
Filters::add('shared_filter', 'text', fn($query) => $query->where(...));
}
}
Reset Filters:
Add a button to reset filters via resetFilters():
<button wire:click="resetFilters">Reset</button>
public function resetFilters()
{
$this->reset('filters');
}
Query String Sync (if config published):
Enable sync_with_query_string: true in config/livewire-filters.php to persist filters in the URL.
Tailwind CSS: Customize published views to match your Tailwind config. Example override:
<!-- resources/views/vendor/livewire-filters/text.blade.php -->
<input type="text" class="border-gray-300 rounded-md shadow-sm focus:border-indigo-500">
Validation:
Add validation rules to $rules in your component:
protected $rules = [
'filters.search' => 'nullable|string|max:255',
];
Localization: Use Laravel’s localization features for filter labels/placeholders:
<x-filters.text label="{{ __('Search') }}" placeholder="{{ __('Type to search...') }}" />
Filter Key Conflicts:
search filters) will overwrite each other.user_search or product_search.Query Application Order:
Filters::add() calls logically or use explicit conditions:
Filters::add('status', 'select', fn($query) => $this->filters['status'] ? $query->where('status', $this->filters['status']) : $query);
Performance with Complex Filters:
LIKE on large tables) can slow queries.->take(100) for pagination.Query String Sync Quirks:
sync_with_query_string is enabled, filters may not update immediately due to Livewire’s reactivity.wire:ignore on the <livewire:filters /> component or manually trigger updates:
document.addEventListener('livewire:init', () => {
Livewire.hook('filter.applied', () => {
window.location.search = new URLSearchParams(window.livewire?.state?.filters).toString();
});
});
Published Views Not Updating:
resources/views/vendor/livewire-filters/) aren’t reflected.php artisan view:clear
Log Filter Values:
Add a temporary dump in updatedFilter* methods:
public function updatedFilterSearch()
{
\Log::debug('Current filters:', $this->filters);
}
Check Query:
Log the final query in render():
\Log::debug('Query:', User::query()->apply($this->filters)->toSql());
Disable Filters Temporarily:
Comment out apply($this->filters) to isolate issues.
Custom Filter Types: Extend the package by creating a new filter component. Example:
php artisan make:livewire CustomFilter
Then register it in Filters::add():
Filters::add('custom', 'custom', fn($query) => $query->where(...));
Override Default Views:
Publish and modify views (e.g., text.blade.php, select.blade.php) to add custom logic like conditional rendering:
@if($showAdvanced)
<div class="advanced-filter">{{ $slot }}</div>
@endif
Add Filter Metadata: Attach metadata (e.g., priority, visibility) to filters using a custom trait:
Filters::add('priority_filter', 'select', fn($query) => $query->where(...))
->meta(['priority' => 1, 'visible' => true]);
Server-Side Filtering: For large datasets, use cursor-based pagination with filters:
public function render()
{
return User::query()
->apply($this->filters)
->cursorPaginate(20);
}
Query String Sync:
GET requests. Avoid using it for POST or complex forms.?filters[search]=test in the URL to verify sync.Default Values:
Set defaults in mount():
public function mount()
{
$this->filters = ['status' => 'active'];
}
Case Sensitivity:
Filter keys in $filters are case-sensitive. Use consistent naming (e.g., camelCase).
How can I help you explore Laravel packages today?