devaction-labs / filterable-package
A Laravel package for filterable traits and classes.
Requires
- php: ^8.2|^8.3|^8.4
- illuminate/cache: ^11|^12
- illuminate/database: ^11|^12
- illuminate/http: ^11|^12
- illuminate/support: ^11|^12
- vlucas/phpdotenv: ^5.6
Requires (Dev)
- laravel/pint: ^1.13.7
- mockery/mockery: ^1.6
- pestphp/pest: ^3
- phpstan/phpstan: ^1.11
- rector/rector: ^1.2
- roave/security-advisories: dev-latest
- symfony/var-dumper: ^6.4.0|^7.0.0
- dev-master
- 1.0.23
- 1.0.22
- v1.0.21
- v1.0.20
- v1.0.19
- v1.0.18
- v1.0.17
- v1.0.16
- v1.0.15
- v1.0.14
- v1.0.13
- v1.0.12
- v1.0.11
- v1.0.10
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- v0.0.2
- v0.0.1
- dev-fix/version-composer
- dev-feat/improve
- dev-feature/readme
- dev-fix/octane
- dev-feature/betwenn
- dev-feature/update-version-php
This package is auto-updated.
Last update: 2025-04-04 19:07:58 UTC
README
A Laravel package for filterable traits and classes. This package provides powerful, dynamic query filtering capabilities directly from incoming requests, especially useful when developing flexible and dynamic APIs.
Features
- Easy Integration: Apply the
Filterable
trait to your Eloquent models. - Flexible Filters: Exact, like, in, between, greater than (gte, gt), less than (lte, lt), JSON, and relationship filters.
- Dynamic Sorting: Customize sorting behavior directly from requests.
- Relationship Filters: Use advanced conditional logic like
whereAny
,whereAll
, andwhereNone
for relational queries. - JSON Support: Directly filter JSON columns with dot-notation.
- Performance Optimizations: Built-in caching and efficient query construction.
- Date Handling: Smart handling of date fields with Carbon integration.
Installation
composer require devaction-labs/filterable-package
Usage
Step 1: Add the Filterable
Trait
namespace App\Models; use Illuminate\Database\Eloquent\Model; use DevactionLabs\FilterablePackage\Traits\Filterable; class Expense extends Model { use Filterable; protected array $filterMap = [ 'search' => 'description', 'date' => 'expense_date', ]; protected array $allowedSorts = ['expense_date', 'amount']; }
Step 2: Applying Filters in Controllers
namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use DevactionLabs\FilterablePackage\Filter; use App\Models\Expense; class ExpenseController extends Controller { public function index() { $expenses = Expense::query() ->filtrable([ Filter::like('description', 'search'), Filter::exact('expense_date', 'date'), Filter::between('expense_date', 'date_range'), Filter::json('attributes', 'user.name', 'LIKE', 'user_name'), Filter::json('attributes', 'user.age', '>', 'user_age'), Filter::relationship('user', 'name')->setValue('John')->with(), Filter::relationship('user', 'name')->whereAny([ ['name', '=', 'John'], ['email', '=', 'john@example.com'], ])->with(), ]) ->customPaginate(false, ['per_page' => 10, 'sort' => '-created_at']); return response()->json($expenses); } }
Available Filters
Direct Filters
- Exact Match:
Filter::exact('status', 'status')
- LIKE Match:
Filter::like('description', 'search')
- IN Clause:
Filter::in('category_id', 'categories')
- Greater Than or Equal:
Filter::gte('amount', 'min_amount')
- Less Than or Equal:
Filter::lte('amount', 'max_amount')
- Between:
Filter::between('created_at', 'date_range')
JSON Filters
- Exact Match:
Filter::json('data', 'user.name', '=', 'user_name')
- LIKE Match:
Filter::json('data', 'user.name', 'LIKE', 'user_name')
Relationship Filters
-
Simple Relationship:
Filter::relationship('user', 'name')->setValue('John')->with()
-
Conditional Logic (
whereAny
,whereAll
,whereNone
):Filter::relationship('user', 'name') ->whereAny([ ['name', '=', 'John'], ['email', '=', 'john@example.com'], ]) ->setValue('John') ->with();
Customizing Pagination and Sorting
Use the provided methods to paginate and sort easily:
$results = Expense::query() ->filtrable([...]) ->customPaginate(false, ['per_page' => 10, 'sort' => '-created_at']);
-
(minus) prefix indicates descending sorting (e.g.,-amount
).
Defining Default Sorting and Allowed Sorts in Model:
protected string $defaultSort = 'amount'; protected array $allowedSorts = ['amount', 'expense_date'];
Custom Filter Mapping
Easily map request parameters to database columns:
protected array $filterMap = [ 'display_name' => 'name', 'date' => 'expense_date', ];
Now, using the parameter filter[display_name]=John
will filter on the name
column.
Advanced Features
Date Handling
The Filterable
package provides sophisticated date handling capabilities:
// Create a date filter that will convert string dates to Carbon instances $dateFilter = Filter::exact('created_at')->castDate(); // Apply to a query $model->filtrable([$dateFilter]);
You can also specify if you want to compare with the start or end of the day:
// Filter by date with time set to 23:59:59 $dateFilter = Filter::exact('created_at')->castDate()->endOfDay(); // Filter by date with time set to 00:00:00 $dateFilter = Filter::exact('created_at')->castDate()->startOfDay();
Custom LIKE Patterns
Customize the pattern used for LIKE filters to match your search requirements:
// Default (contains): '%value%' $filter = Filter::like('description', 'search'); // Starts with: 'value%' $filter = Filter::like('description', 'search')->setLikePattern('{{value}}%'); // Ends with: '%value' $filter = Filter::like('description', 'search')->setLikePattern('%{{value}}');
JSON Field Filtering with Database-Specific Optimizations
The package automatically applies the correct JSON extraction syntax based on your database:
// The query will use the appropriate syntax for your database $filter = Filter::json('attributes', 'user.age', '>', 'min_age'); // Manually specify database driver if needed $filter = Filter::json('attributes', 'user.age', '>', 'min_age')->setDatabaseDriver('mysql');
Advanced Relationship Filtering with Conditional Logic
Apply complex conditions to your relationship filters:
// Match if ANY condition is true (OR logic) $filter = Filter::relationship('user', 'name') ->whereAny([ ['name', '=', 'John'], ['email', '=', 'john@example.com'], ]) ->with(); // Match if ALL conditions are true (AND logic) $filter = Filter::relationship('user', 'name') ->whereAll([ ['name', '=', 'John'], ['active', '=', true], ]) ->with(); // Match if NONE of the conditions are true (NOT logic) $filter = Filter::relationship('user', 'name') ->whereNone([ ['banned', '=', true], ['deleted', '=', true], ]) ->with();
Performance Optimizations
The Filterable
trait includes several performance optimizations:
- Efficient caching of attribute and relationship validations
- Optimized handling of relationship filters
- Smart deduplication of eager-loaded relationships
- Specialized handling for simple equality relationship filters
These optimizations are automatically applied when you use the trait, ensuring your filterable queries remain performant even with complex filter combinations.
Complete Usage Example
Here's a comprehensive example showing how to use multiple features together:
namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Models\Product; use DevactionLabs\FilterablePackage\Filter; use Illuminate\Http\Request; class ProductController extends Controller { public function index(Request $request) { // Build a complex query using the Filterable trait $products = Product::query() // Define allowed sort fields and default sort ->allowedSorts(['name', 'price', 'created_at'], '-created_at') // Define filter field mappings ->filterMap([ 'search' => 'name', 'price_range' => 'price', 'date' => 'created_at', 'status_code' => 'status', ]) // Apply filters ->filtrable([ // Basic filters Filter::like('name', 'search') ->setLikePattern('{{value}}%'), // Custom LIKE pattern (starts with) // Numeric range filter Filter::between('price', 'price_range'), // Date filter with Carbon conversion Filter::exact('created_at', 'date') ->castDate() ->endOfDay(), // Automatically set time to end of day // JSON field filtering Filter::json('attributes', 'specs.color', 'LIKE', 'color') ->setDatabaseDriver('mysql'), Filter::json('attributes', 'specs.weight', '>', 'min_weight') ->setDatabaseDriver('mysql'), // Relationship filter with eager loading Filter::relationship('category', 'slug', '=', 'category') ->with(), // Eager load this relationship // Complex relationship filter with conditional logic Filter::relationship('tags', 'name') ->whereAny([ ['name', '=', 'featured'], ['name', '=', 'sale'], ]) ->with() ->setValue('has_special_tag'), // Custom value for this filter // Multiple criteria for user permissions Filter::relationship('user', 'id') ->whereAll([ ['active', '=', true], ['role', '=', 'admin'], ]) ->setValue(auth()->id()), ]) // Apply pagination with custom parameters ->customPaginate(false, [ 'per_page' => $request->input('per_page', 15), 'sort' => $request->input('sort', '-created_at'),===========fffdfdfsdddddddddddsdsdsdsads ]); return response()->json($products); } }
Supported Databases for JSON Filters
- MySQL
- PostgreSQL
- SQLite
The package automatically detects the database driver from your configuration.
Testing
composer test
Contributing
Please see CONTRIBUTING.md for details.
Security
If you discover any security related issues, please email alex@devaction.com.br instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.