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

Filter Bundle Laravel Package

austral/filter-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require austral/filter-bundle
    

    Ensure austral/tools-bundle and austral/form-bundle are also installed (auto-included as dependencies).

  2. Register the Bundle: Add to config/app.php under providers:

    Austral\FilterBundle\FilterBundle::class,
    
  3. First Use Case: Filter a collection of Austral entities (e.g., Post, User). Example in a controller:

    use Austral\FilterBundle\Filter\FilterManager;
    
    public function index(FilterManager $filterManager)
    {
        $posts = $filterManager->apply(
            Post::query(),
            request()->query->all() // or custom filter rules
        );
        return view('posts.index', compact('posts'));
    }
    
  4. Key Files:

    • config/filter.php: Default configuration (rules, mappings).
    • src/FilterBundle/Resources/config/services.yaml: Service definitions.
    • tests/: Reference implementations for edge cases.

Implementation Patterns

Core Workflows

  1. Request-Based Filtering: Bind query parameters to filter rules in config/filter.php:

    # config/filter.php
    filters:
        posts:
            - { name: 'title', operator: 'like', field: 'title' }
            - { name: 'published', operator: 'eq', field: 'is_published', type: 'bool' }
    

    Use in controller:

    $filtered = $filterManager->apply(Post::query(), request()->query);
    
  2. Custom Filter Rules: Extend Austral\FilterBundle\Filter\AbstractFilter:

    namespace App\Filters;
    
    use Austral\FilterBundle\Filter\AbstractFilter;
    
    class CustomDateFilter extends AbstractFilter
    {
        protected function applyFilter($query, $value)
        {
            return $query->whereDate('created_at', $value);
        }
    }
    

    Register in config/filter.php:

    filters:
        posts:
            - { name: 'date', class: App\Filters\CustomDateFilter }
    
  3. Form Integration: Use Austral\FormBundle to render filter inputs:

    use Austral\FormBundle\Form\FilterForm;
    
    $form = $this->createForm(FilterForm::class, null, [
        'filters' => $filterManager->getFilters('posts')
    ]);
    
  4. API Filtering: Validate and apply filters via DTOs (e.g., Symfony Serializer):

    $dto = $serializer->deserialize(request()->json->all(), FilterDto::class, 'json');
    $filtered = $filterManager->apply(Post::query(), $dto->getFilters());
    

Integration Tips

  • Laravel Scout: Combine with scout:filter for search-as-you-type:
    $scoutResults = Post::search(request('q'))->filter($filterManager);
    
  • Pagination: Use simple-pagination or laravel-pagination after filtering:
    $posts = $filterManager->apply(Post::query(), request()->query)
        ->paginate(10);
    
  • Caching: Cache filtered results with Cache::remember:
    $posts = Cache::remember("posts_{$cacheKey}", now()->addHour(), function() use ($filterManager) {
        return $filterManager->apply(Post::query(), request()->query)->get();
    });
    

Gotchas and Tips

Common Pitfalls

  1. Operator Mismatches:

    • like vs contains: Ensure config/filter.php operators align with your DB dialect (e.g., LIKE vs ILIKE in PostgreSQL).
    • Fix: Override AbstractFilter::getOperator() for custom SQL.
  2. Case Sensitivity:

    • eq on strings may fail if DB collation differs (e.g., utf8mb4_bin vs utf8mb4_unicode_ci).
    • Fix: Use lower() in applyFilter:
      return $query->whereRaw('LOWER(field) = LOWER(:value)', ['value' => $value]);
      
  3. Nested Relationships:

    • Filtering on user.email requires eager loading:
      $posts = $filterManager->apply(
          Post::with('user'),
          request()->query
      );
      
    • Fix: Use with() or loadMissing() to avoid N+1 queries.
  4. Boolean Fields:

    • eq:1 may not work for boolean fields (stored as 0/1 or true/false).
    • Fix: Cast in applyFilter:
      return $query->where('is_active', (bool)$value);
      
  5. Mass Assignment:

    • Filter parameters might expose sensitive fields (e.g., deleted_at).
    • Fix: Whitelist fields in config/filter.php:
      filters:
          posts:
              allowed_fields: ['title', 'published']
      

Debugging Tips

  • Log Raw Queries: Enable Laravel’s query logging in AppServiceProvider:

    public function boot()
    {
        if (app()->environment('local')) {
            DB::enableQueryLog();
        }
    }
    

    Dump logs after filtering:

    dd(DB::getQueryLog());
    
  • Validate Inputs: Use Symfony’s Validator to sanitize filter inputs:

    use Symfony\Component\Validator\Validator\ValidatorInterface;
    
    $validator = app(ValidatorInterface::class);
    $errors = $validator->validate($filterDto);
    if ($errors->count()) { /* Handle */ }
    

Extension Points

  1. Custom Filter Types: Add support for array or json fields:

    class JsonFilter extends AbstractFilter
    {
        protected function applyFilter($query, $value)
        {
            return $query->whereJsonContains('metadata', $value);
        }
    }
    
  2. Dynamic Filter Sources: Load filters from a database table:

    $dynamicFilters = FilterRule::where('model', 'Post')->get();
    $filtered = $filterManager->apply(Post::query(), request()->query, $dynamicFilters);
    
  3. Event Listeners: Hook into FilterApplied events to log or modify queries:

    use Austral\FilterBundle\Event\FilterAppliedEvent;
    
    public function onFilterApplied(FilterAppliedEvent $event)
    {
        if ($event->getModel() === Post::class) {
            $event->getQuery()->orderBy('title');
        }
    }
    

    Register in EventServiceProvider:

    protected $listen = [
        FilterAppliedEvent::class => [
            FilterListener::class,
        ],
    ];
    
  4. Testing: Mock FilterManager in PHPUnit:

    $filterManager = $this->createMock(FilterManager::class);
    $filterManager->method('apply')
        ->willReturn(Post::factory()->count(3));
    $this->app->instance(FilterManager::class, $filterManager);
    

Configuration Quirks

  • Default Values: Override defaults in config/filter.php:

    default_operator: 'contains' # Overrides global 'eq'
    strict_mode: true           # Throws exceptions on invalid filters
    
  • Performance: Disable strict_mode in production to avoid exceptions on malformed requests:

    strict_mode: false
    
  • Localization: Translate operator labels (e.g., eq → "equals") in resources/lang/en/filter.php:

    return [
        'operators' => [
            'eq' => 'is equal to',
            'like' => 'contains',
        ],
    ];
    

    Use in Blade:

    {{ __('filter.operators.'.$filter->getOperator()) }}
    
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.
ilhamsyabani/laravel-volt-starter
thethunderturner/filament-latex
ghostcompiler/laravel-querybuilder
webrek/laravel-telescope-mongodb
anousss007/blatui
zatona-eg/zatona-eg-api
cocosmos/filament-sticky-save-bar
patrickbussmann/oauth2-apple
3brs/enterprise-security-bundle
anousss007/vigilance
supportpal/eloquent-model
ardenexal/fhir-models
laravel-at/laravel-image-sanitize
romalytar/yammi-audit-log-laravel
ardenexal/fhir-validation
arshaviras/weather-widget
laravel-chronicle/core
sunchayn/nimbus
daikazu/eloquent-salesforce-objects
unseen-codes/chat