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

Query Filters Laravel Package

cerbero/query-filters

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require cerbero/query-filters
    

    Publish the config (if needed):

    php artisan vendor:publish --tag=query_filters_config
    
  2. Generate a Filter Class:

    php artisan make:query-filter UserFilter --model=User
    

    This creates app/QueryFilters/UserFilter.php (or your configured path).

  3. Define Filters: Edit the generated file to add rules. Example:

    public function apply($query)
    {
        if ($this->has('name')) {
            $query->where('name', 'like', '%' . $this->get('name') . '%');
        }
    
        if ($this->has('active')) {
            $query->where('active', $this->get('active'));
        }
    }
    
  4. Use in Controller:

    use Cerbero\QueryFilters\Traits\FiltersRequests;
    use App\QueryFilters\UserFilter;
    
    class UserController extends Controller
    {
        use FiltersRequests;
    
        public function index(UserFilter $filter)
        {
            $users = $filter->apply($this->userModel->newQuery());
            return response()->json($users->get());
        }
    }
    
  5. Route Request:

    Route::get('/users', [UserController::class, 'index']);
    

    Now call /users?name=John&active=1 to filter.


First Use Case: API Filtering

  • Scenario: Build a /users API endpoint with dynamic filtering.
  • Steps:
    1. Generate UserFilter.
    2. Add rules for name, email, role, etc.
    3. Inject the filter into the controller.
    4. Test with query params like ?role=admin&active=true.

Implementation Patterns

Core Workflow

  1. Filter Generation:

    • Use make:query-filter for each model.
    • Place filters in a logical namespace (e.g., App\QueryFilters).
  2. Rule Definition:

    • Basic Rules: Use has() and get() to check and retrieve params.
      if ($this->has('status')) {
          $query->where('status', $this->get('status'));
      }
      
    • Advanced Rules: Chain conditions or use closures.
      if ($this->has('created_at')) {
          $query->whereDate('created_at', $this->get('created_at'));
      }
      
    • Validation: Add Laravel validation rules in rules() method.
      public function rules()
      {
          return [
              'name' => 'sometimes|string|max:255',
              'active' => 'sometimes|boolean',
          ];
      }
      
  3. Controller Integration:

    • Use the FiltersRequests trait to auto-resolve filters.
    • Pass the filter to your query:
      public function index(UserFilter $filter)
      {
          $query = $filter->apply(User::query());
          return $query->paginate(10);
      }
      
  4. API Resource Integration:

    • Combine with Laravel API Resources for structured responses.
      return UserResource::collection($filter->apply(User::query())->paginate());
      

Integration Tips

  1. Dynamic Filtering:

    • Use filter() method to apply filters conditionally:
      $filter->filter($query, ['name', 'email']);
      
  2. Nested Filters:

    • For complex relationships, nest filters in separate classes. Example: PostFilter with a UserFilter for author filtering.
  3. Caching:

    • Cache filter results if queries are expensive:
      return Cache::remember("users_{$filter->getCacheKey()}", now()->addHours(1), function () use ($filter) {
          return $filter->apply(User::query())->get();
      });
      
  4. Testing:

    • Test filters with TestCase and mock requests:
      $response = $this->get('/users?name=John');
      $response->assertJsonCount(1, 'data');
      
  5. Frontend Integration:

    • Use libraries like React-Table or Vue Good Table to dynamically build query params from UI filters.

Gotchas and Tips

Pitfalls

  1. Case Sensitivity:

    • Query params are case-sensitive by default. Use strtolower() if needed:
      if ($this->has('role')) {
          $query->where('role', strtolower($this->get('role')));
      }
      
  2. SQL Injection:

    • Always use Eloquent methods (where, orWhere) instead of raw SQL or concatenation.
  3. Empty Parameters:

    • Handle cases where params exist but are empty:
      if ($this->has('name') && $this->get('name') !== '') {
          $query->where('name', 'like', '%' . $this->get('name') . '%');
      }
      
  4. Performance:

    • Avoid LIKE on large columns (e.g., description) without a full-text index.
    • Use select() to limit columns if only specific fields are needed.
  5. Validation Overrides:

    • If using rules(), ensure sometimes is included for optional params:
      'name' => 'sometimes|string|max:255', // Optional
      'active' => 'required|boolean',       // Required
      

Debugging

  1. Log Filter Inputs: Add debug logs to see raw params:

    public function apply($query)
    {
        \Log::info('Filter params:', $this->all());
        // ...
    }
    
  2. Check SQL Queries: Use Laravel Debugbar or DB::enableQueryLog():

    DB::enableQueryLog();
    $filter->apply(User::query());
    \Log::info(DB::getQueryLog());
    
  3. Validate Requests: Ensure params are being passed correctly. Test with Postman or cURL:

    curl "http://yourapp.test/users?name=John&active=1"
    

Tips

  1. Reusable Filter Logic: Extract common filter logic into base classes:

    class BaseFilter extends QueryFilter
    {
        protected function filterByStatus($query)
        {
            if ($this->has('status')) {
                $query->where('status', $this->get('status'));
            }
            return $query;
        }
    }
    
  2. Document Filters: Add PHPDoc comments to generated filters for clarity:

    /**
     * Filters users by name, active status, and role.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function apply($query)
    {
        // ...
    }
    
  3. Default Values: Set defaults in the filter constructor:

    public function __construct()
    {
        $this->defaults = [
            'active' => true, // Default to active users
        ];
    }
    
  4. Soft Deletes: Handle soft-deleted models with withTrashed():

    $query = $filter->apply(User::withTrashed()->newQuery());
    
  5. Ordering: Combine with Laravel's orderBy for sorted results:

    $query = $filter->apply(User::query())->orderBy('created_at', 'desc');
    
  6. Pagination: Use Laravel's paginator for API responses:

    return $filter->apply(User::query())->paginate(15);
    
  7. Localization: For multilingual apps, translate filter labels:

    $this->labels = [
        'name' => __('Name'),
        'active' => __('Active'),
    ];
    
  8. Testing Filters: Use fromRequest() to test filter logic:

    public function test_filter_by_name()
    {
        $request = new Request(['name' => 'John']);
        $filter = new UserFilter($request);
        $query = $filter->apply(User::query());
        $query->toSql(); // Check generated SQL
    }
    
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.
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours