Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Livewire Filterable Laravel Package

devaction-labs/livewire-filterable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the package:

    composer require devaction-labs/livewire-filterable
    

    Ensure your project meets requirements: PHP 8.5+, Laravel 11/12, Livewire 4.

  2. Add traits to your Eloquent model:

    use DevactionLabs\LivewireFilterable\Traits\Filterable;
    use DevactionLabs\LivewireFilterable\Concerns\HasCustomPagination;
    
    class Customer extends Model
    {
        use Filterable, HasCustomPagination;
    }
    
  3. Create a Livewire component:

    use DevactionLabs\LivewireFilterable\Concerns\LivewireFilterable;
    use DevactionLabs\LivewireFilterable\Filter;
    
    class CustomerList extends Component
    {
        use LivewireFilterable;
    
        public string $name = '';
    
        public function render()
        {
            return Customer::query()
                ->filterable([Filter::ilike('name')])
                ->customPaginate()
                ->toLivewireResponse();
        }
    }
    
  4. Bind inputs to public properties in Blade:

    <input wire:model.live="name" type="text" placeholder="Search...">
    

First Use Case

Build a reactive search interface for a Product table with:

  • Text search (name, description)
  • Price range filtering
  • Category dropdown
  • Debounced input (500ms)
class ProductSearch extends Component
{
    use LivewireFilterable;

    public string $search = '';
    public array $priceRange = [];
    public ?int $categoryId = null;

    public function render()
    {
        return Product::query()
            ->filterable([
                Filter::ilike('name')->debounce(500),
                Filter::between('price', 'priceRange'),
                Filter::exact('category_id', 'categoryId')
            ])
            ->customPaginate()
            ->toLivewireResponse();
    }
}

Implementation Patterns

Core Workflow

  1. Property Binding: Public Livewire properties automatically bind to filters when named identically to database columns.

    // Automatically filters `email` column
    public string $email = '';
    
  2. Filter Composition: Chain filters using the fluent Filter class:

    Filter::ilike('name')
        ->debounce(500)
        ->caseInsensitive()
    
  3. Pagination Integration:

    ->customPaginate('cursor', $this->perPage)
    

Common Patterns

Relationship Filtering

// Filter products by related category slug
Filter::relationship('category', 'slug', '=', 'categorySlug')
    ->with(['category' => function($query) {
        $query->select('id', 'name');
    }])

Date Handling

// Filter orders from today
public string $today = '';

Filter::exact('created_at', 'today')
    ->castDate()
    ->startOfDay()
    ->endOfDay()

Dynamic Filtering

// Conditionally apply filters
if ($this->showActiveOnly) {
    $filters[] = Filter::exact('is_active', true);
}

URL Persistence

use Livewire\Attributes\Url;

class Search extends Component
{
    #[Url] public string $query = '';
    #[Url(as: 'min_price')] public ?float $minPrice = null;
}

Integration Tips

  1. Form Validation:

    protected $rules = [
        'name' => 'sometimes|string|max:255',
        'priceRange' => 'sometimes|array|min:2',
    ];
    
  2. Debouncing:

    • Blade: wire:model.live.debounce.500ms
    • Programmatic: Filter::like('name')->debounce(500)
  3. Reset Filters:

    public function resetFilters()
    {
        $this->reset(['name', 'email', 'categoryId']);
    }
    
  4. Custom Property Mapping:

    Filter::ilike('full_name', 'searchName')
    
  5. Performance Optimization:

    // Use cursor pagination for large datasets
    ->customPaginate('cursor', 50)
    

Gotchas and Tips

Pitfalls

  1. Property Naming Mismatch:

    • Issue: public string $userName won't filter username column.
    • Fix: Use Filter::exact('username', 'userName').
  2. Debounce Conflicts:

    • Issue: Multiple debounced inputs may cause race conditions.
    • Fix: Use unique debounce values or disable debounce for critical filters.
  3. Relationship Loading:

    • Issue: Forgetting ->with() can cause N+1 queries.
    • Fix: Always include ->with() for relationship filters.
  4. Date Handling:

    • Issue: castDate() without timezone awareness may cause off-by-day issues.
    • Fix: Set timezone explicitly:
      Filter::exact('created_at')
          ->castDate()
          ->setTimezone('America/New_York')
      
  5. Full-Text Search:

    • Issue: PostgreSQL tsvector requires proper indexing.
    • Fix: Create GIN index and trigger as shown in docs.

Debugging Tips

  1. Query Logging: Enable Laravel query logging to verify filters:

    \DB::enableQueryLog();
    $results = Product::query()->filterable([...])->get();
    \Log::info(\DB::getQueryLog());
    
  2. Filter Validation: Use Filter::validate() to check filter syntax:

    try {
        Filter::ilike('name')->validate();
    } catch (\InvalidArgumentException $e) {
        // Handle error
    }
    
  3. Performance Profiling: Compare execution times:

    $start = microtime(true);
    $results = Product::query()->filterable([...])->get();
    $time = microtime(true) - $start;
    \Log::info("Query took {$time}s");
    

Configuration Quirks

  1. Database-Specific Features:

    • ilike() behaves differently across databases:
      • PostgreSQL: Native ILIKE
      • MySQL: Uses LOWER()
      • SQLite: Standard LIKE (case-insensitive by default)
  2. JSON Field Handling:

    • Database driver must be specified for JSON filters:
      Filter::json('metadata', 'specs.weight', '>', 'min_weight')
          ->setDatabaseDriver('mysql') // or 'pgsql'
      
  3. Pagination Types:

    • cursor pagination requires unique, sortable columns.
    • simple pagination is fastest but lacks total count.

Extension Points

  1. Custom Filter Types: Extend the Filter class to add domain-specific filters:

    class CustomFilter extends Filter
    {
        public static function customLogic(string $column): self
        {
            return new static($column, 'custom_logic');
        }
    
        protected function apply(Builder $query, $value): Builder
        {
            return $query->where($this->column, 'custom_logic_value');
        }
    }
    
  2. Filter Events: Listen for filter application:

    Filter::listen(function (Filter $filter, Builder $query) {
        \Log::debug("Applying filter: {$filter->getColumn()}");
    });
    
  3. Model-Specific Filtering: Override getFilterableAttributes() in your model:

    public function getFilterableAttributes(): array
    {
        return ['name', 'email', 'created_at'];
    }
    
  4. Custom Pagination Logic: Extend HasCustomPagination trait:

    class CustomPagination
    {
        public function apply(Builder $query, string $type, int $perPage): Builder
        {
            // Custom logic
        }
    }
    

Pro Tips

  1. Use #[NoDiscard] Attributes: Leverage PHP 8.5's #[\NoDiscard] for fluent method chains:

    Filter::ilike('name')->debounce(500)->caseInsensitive();
    
  2. Pipe Operator for Chaining:

    $filters = collect([])
        ->pipe(fn($c) => $c->push(Filter::ilike('name')))
        ->pipe(fn($c) => $c->push(Filter::exact('status')));
    
  3. Type-Safe Filtering: Use PHP 8.5's typed properties:

    public string $search = '';
    public int $minPrice = 0;
    public ?DateTimeInterface $startDate = null;
    
  4. Bulk Filter Operations:

    $filters = collect([
        'name' => Filter::ilike('name'),
        'price' => Filter::gt('price'),
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
babenkoivan/elastic-client
innmind/static-analysis
innmind/coding-standard
datacore/hub-sdk
alengo/sulu-http-cache-bundle
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
imbo/imbo-coding-standard
visualbuilder/filament-lottie
servicioslineaonce/starter-kit
atomcoder/laravel-reorderable
irajul/filament-shadcn-theme
agtp/agtp-php
agtp/mod-php
centraldesktop/protobuf-php