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

Laravel Model Filter Laravel Package

lacodix/laravel-model-filter

Filter, search, and sort Eloquent models with reusable filter classes and query-string support. Includes built-in types (string, date, number, enum), relation/nested relation filtering, custom complex logic, and filter visualisation.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require lacodix/laravel-model-filter
    

    Publish config (if needed):

    php artisan vendor:publish --provider="Lacodix\LaravelModelFilter\ServiceProvider"
    
  2. First Filter: Generate a filter for a date field (e.g., created_at):

    php artisan make:filter CreatedAfterFilter --type=date --field=created_at
    
  3. Apply to Model: Add the HasFilters trait and register the filter in your model (e.g., Post):

    use Lacodix\LaravelModelFilter\Traits\HasFilters;
    
    class Post extends Model
    {
        use HasFilters;
    
        protected array $filters = [
            \App\Models\Filters\CreatedAfterFilter::class,
        ];
    }
    
  4. First Query: Filter posts created after January 1, 2023:

    Post::filter(['created_after_filter' => '2023-01-01'])->get();
    

    Or via URL:

    /posts?created_after_filter=2023-01-01
    
  5. Search Setup: Enable search for a model (e.g., Post):

    use Lacodix\LaravelModelFilter\Traits\IsSearchable;
    
    class Post extends Model
    {
        use IsSearchable;
    
        protected array $searchable = ['title', 'content'];
    }
    

    Search for "test":

    Post::search('test')->get();
    

    Or via URL:

    /posts?search=test
    

Implementation Patterns

Core Workflows

  1. Filter Creation:

    • Use make:filter for common types (date, text, select, etc.).
    • Extend base filter classes (e.g., DateFilter, TextFilter) for custom logic.
    • Example: Custom StatusFilter for enum-like fields:
      class StatusFilter extends SelectFilter
      {
          protected string $field = 'status';
      
          public function options(): array
          {
              return ['draft', 'published', 'archived'];
          }
      }
      
  2. Query Integration:

    • Direct Filtering:
      Post::filter(['status_filter' => 'published'])->get();
      
    • Query String Parsing:
      Post::filterByQueryString()->get(); // Parses request query
      
    • Grouped Filters (e.g., frontend/backend):
      Post::filter(['hot_filter' => 'true'], 'frontend')->get();
      
  3. Search Patterns:

    • Basic Search:
      Post::search('laravel')->get();
      
    • Field-Specific Search:
      Post::search('laravel', ['title'])->get();
      
    • Advanced Modes (e.g., case-sensitive starts-with):
      protected array $searchable = [
          'title' => SearchMode::STARTS_WITH_CASE_SENSITIVE,
      ];
      
  4. Relation Filtering:

    • Use RunsOnRelation trait for nested filters:
      class Comment extends Model
      {
          use HasFilters, RunsOnRelation;
      
          protected array $filters = [
              \App\Models\Filters\AuthorFilter::class,
          ];
      }
      
    • Apply to parent model:
      Post::whereHas('comments', fn($q) =>
          $q->filter(['author_filter' => 'john'])
      )->get();
      
  5. Visualization:

    • Render all filters for a model:
      <x-lacodix-filter::model-filters :model="Post::class" />
      
    • Customize appearance:
      <x-lacodix-filter::model-filters
          :model="Post::class"
          method="post"
          :action="route('posts.filter')"
      />
      
  6. Dynamic Filtering:

    • Conditional visibility (e.g., feature flags):
      public function visible(): bool
      {
          return Feature::active('advanced_filters');
      }
      

Integration Tips

  1. API Usage:

    • Parse query strings in API routes:
      $filters = request()->query('filter');
      $posts = Post::filter($filters)->get();
      
    • Use filterByQueryString() for automatic parsing.
  2. Form Integration:

    • Combine with Laravel Collective or Livewire for dynamic filtering:
      <form wire:submit.prevent="filter">
          <x-lacodix-filter::model-filters :model="$posts" />
          <button type="submit">Apply</button>
      </form>
      
  3. Testing:

    • Mock filters in unit tests:
      $filter = new CreatedAfterFilter();
      $this->assertEquals('created_at', $filter->field());
      
    • Test query string parsing:
      $query = Post::filterByQueryString();
      $query->toSql(); // Verify generated SQL
      
  4. Performance:

    • Use select() to limit fetched columns:
      Post::filter($filters)->select(['id', 'title'])->get();
      
    • Avoid N+1 issues with eager loading:
      Post::with('comments')->filter($filters)->get();
      
  5. Custom Components:

    • Override default Blade components:
      protected string $component = 'custom-filter';
      
    • Publish views for full control:
      php artisan vendor:publish --tag=lacodix-filter-views
      

Gotchas and Tips

Pitfalls

  1. Field Name Mismatches:

    • Ensure $field in filters matches the database column name.
    • For relations, qualify field names (e.g., user.name for user relation).
  2. Query String Parsing:

    • Default query parameter names are filter[] for filters and search for searches.
    • Customize in config/model-filter.php:
      'filter_query_value_name' => 'f',
      'search_query_value_name' => 'q',
      
  3. Case Sensitivity:

    • Search modes like LIKE_CASE_SENSITIVE may impact performance.
    • Test with large datasets to avoid timeouts.
  4. Grouping Confusion:

    • Groups must be explicitly specified in queries:
      // ❌ Fails silently
      Post::filter(['hot_filter' => 'true'])->get();
      
      // ✅ Works
      Post::filter(['hot_filter' => 'true'], 'frontend')->get();
      
  5. Relation Filtering:

    • RunsOnRelation requires proper field qualification. For example:
      // ❌ Fails (ambiguous column)
      $q->filter(['author_filter' => 'john']);
      
      // ✅ Works (qualified)
      $q->filter(['comments.author_filter' => 'john']);
      
  6. Search Performance:

    • Avoid CONTAINS_ALL/CONTAINS_ANY on large text fields without full-text indexes.
    • Use database-specific optimizations (e.g., PostgreSQL tsvector).
  7. Filter Mode Limitations:

    • Not all modes work with all filter types (e.g., BETWEEN for TextFilter).
    • Check the documentation for type-specific modes.

Debugging Tips

  1. Log Generated SQL:

    \DB::enableQueryLog();
    Post::filter($filters)->get();
    \Log::info(DB::getQueryLog());
    
  2. Inspect Filter Inputs:

    • Dump parsed query strings:
      dd(request()->query());
      
  3. Validate Filter Values:

    • Override validate() in custom filters:
      public function validate(string $value): void
      {
          if (!in_array($value, $this->options())) {
              throw new \InvalidArgumentException("Invalid status: {$value}");
          }
      }
      
  4. Check Visibility:

    • Debug visible() method:
      $filter = new CreatedAfterFilter();
      dd($filter->visible()); // Should return bool
      

Extension Points

  1. Custom Filter Types:

    • Extend BaseFilter for new types:
      class CustomFilter extends BaseFilter
      {
          protected string $type = 'custom';
      
          public function apply(Builder $query, string $value): void
          {
              // Custom logic
          }
      }
      
  2. Dynamic Field Mapping:

    • Override field() to map input to database columns:
      public function field(): string
      {
          return $this->input === 'user_name' ? 'users.name' : $this->field;
      }
      
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle