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

Eloquent Filtering Laravel Package

indexzer0/eloquent-filtering

Define allowed filters on your Eloquent models and apply them from simple arrays or request data—no custom query logic. Supports complex, type-based filtering for APIs and dashboards on Laravel 10+ / PHP 8.2+.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require indexzer0/eloquent-filtering
    php artisan eloquent-filtering:install
    
  2. Implement IsFilterable in your model:

    use IndexZer0\EloquentFiltering\Contracts\IsFilterable;
    use IndexZer0\EloquentFiltering\Traits\Filterable;
    
    class Product extends Model implements IsFilterable
    {
        use Filterable;
    
        public function allowedFilters(): AllowedFilterList
        {
            return Filter::only(
                Filter::field('name', [FilterType::EQUAL]),
                Filter::field('price', [FilterType::GREATER_THAN, FilterType::LESS_THAN])
            );
        }
    }
    
  3. Apply filters via HTTP request:

    $filters = request()->input('filters', []);
    $products = Product::filter($filters)->get();
    

First Use Case

API Endpoint Example:

// routes/api.php
Route::get('/products', function () {
    $products = Product::filter(request()->input('filters', []))
        ->paginate(15);

    return response()->json($products);
});

Request:

{
    "filters": [
        {"type": "$eq", "target": "name", "value": "Laptop"},
        {"type": "$gt", "target": "price", "value": 500}
    ]
}

Implementation Patterns

1. Defining Filters

  • Basic Field Filtering:
    Filter::field('status', [FilterType::EQUAL, FilterType::NOT_EQUAL])
    
  • Nested Relationships:
    Filter::field('user->name', [FilterType::EQUAL])
    
  • JSON Fields:
    Filter::field('metadata->*.price', [FilterType::GREATER_THAN])
    

2. Conditional Filters

  • OR/AND Logic (auto-allowed):
    $filters = [
        ["type": "$or", "filters": [
            ["type": "$eq", "target": "status", "value": "active"],
            ["type": "$eq", "target": "status", "value": "pending"]
        ]]
    ];
    

3. Dynamic Filtering from Requests

  • API Resource:
    public function toArray($request)
    {
        $this->applyFilters($request->input('filters', []));
        return parent::toArray($request);
    }
    

4. Integration with Form Requests

public function rules()
{
    return [
        'filters' => 'sometimes|array',
    ];
}

public function authorize()
{
    return true;
}

5. Combining with Eloquent Scopes

public function scopeActive($query)
{
    return $query->where('active', true);
}

// Usage:
Product::filter($filters)->active()->get();

6. Custom Filter Types

Extend FilterType for domain-specific logic:

class CustomFilterType extends FilterType
{
    public static function handle($query, $value, $operator, $target)
    {
        return $query->where($target, 'LIKE', "%{$value}%");
    }
}

Gotchas and Tips

Common Pitfalls

  1. Target Mismatch:

    • Ensure target in filters matches exact column names (including relationships).
    • Example: user.name vs user->name (use -> for relationships).
  2. JSON Path Wildcards:

    • Use ->* for arrays in JSON fields (e.g., metadata->*.price).
    • Gotcha: MySQL 8+ required for JSON_LENGTH and wildcards.
  3. Case Sensitivity:

    • LIKE is case-insensitive by default in most databases. Use ILIKE for PostgreSQL if needed.
  4. Performance:

    • Avoid complex filters on large datasets without proper indexing.
    • Tip: Use ->select(['id', 'name']) to limit columns in filtered queries.
  5. Sorting Limitation:

    • Sorting is in "infancy" (per docs). Avoid relying on it for production-critical apps.

Debugging Tips

  • Inspect Raw SQL:
    $query = Product::filter($filters);
    dd($query->toSql(), $query->getBindings());
    
  • Validate Filters: Use Filter::validate() to check allowed filters before applying:
    if (!Filter::validate($filters, $this->allowedFilters())) {
        abort(400, 'Invalid filters');
    }
    

Configuration Quirks

  • Default Behavior:

    • allowedFilters() must return an AllowedFilterList (use Filter::only() or Filter::all()).
    • Note: $or and $and are always allowed (no need to define them).
  • Customizing Filter Types: Override FilterType::handle() for global changes or register new types via service provider:

    FilterType::macro('custom', function ($query, $value, $operator, $target) {
        // Custom logic
    });
    

Extension Points

  1. Filter Validation: Override validateFilter() in your model for custom rules:

    protected function validateFilter(array $filter): bool
    {
        if ($filter['type'] === '$eq' && $filter['target'] === 'name') {
            return strlen($filter['value']) > 2;
        }
        return true;
    }
    
  2. Filter Transformation: Use Filter::transform() to modify incoming filters:

    $transformed = Filter::transform($filters, function ($filter) {
        if ($filter['type'] === '$like') {
            $filter['value'] = strtoupper($filter['value']);
        }
        return $filter;
    });
    
  3. Event Listeners: Listen for eloquent.filtering events to log or modify queries:

    Event::listen('eloquent.filtering', function ($query, $filters) {
        // Log or modify $query before execution
    });
    

Pro Tips

  • Reusable Filter Sets: Cache allowed filters for complex models:

    private static $allowedFilters;
    
    public function allowedFilters(): AllowedFilterList
    {
        return self::$allowedFilters ??= Filter::only(
            // ... define once
        );
    }
    
  • API Documentation: Auto-generate OpenAPI specs for filters using Filter::toOpenApiSchema().

  • Testing: Use FilterTestCase for unit tests:

    use IndexZer0\EloquentFiltering\Tests\FilterTestCase;
    
    class ProductFilterTest extends FilterTestCase
    {
        public function testNameFilter()
        {
            $this->assertFiltered('name', '$eq', 'Laptop');
        }
    }
    
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.
aashan/pimcore-mcp-bundle
solution-forest/ai-kit-core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin