## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require jerome/filterable
Publish the config (if needed):
php artisan vendor:publish --provider="Filterable\FilterableServiceProvider"
First Use Case:
Create a filter for a model (e.g., UserFilter):
php artisan make:filter UserFilter --model=User
This generates a stub in app/Filters/UserFilter.php.
Apply to a Controller:
use Filterable\Filterable;
use App\Filters\UserFilter;
class UserController extends Controller
{
use Filterable;
public function index(UserFilter $filter)
{
$users = $filter->apply($this->userRepository->all());
return response()->json($users);
}
}
Test with API Request: Send a request with query params like:
/users?name=John&status=active
The package automatically maps these to filter methods.
Define Filters:
Extend Filterable\Filter and use traits like Filterable\Concerns\FiltersByRequest:
namespace App\Filters;
use Filterable\Filter;
use Filterable\Concerns\FiltersByRequest;
class UserFilter extends Filter
{
use FiltersByRequest;
protected $model = User::class;
public function name($query, $name)
{
return $query->where('name', 'like', "%{$name}%");
}
public function status($query, $status)
{
return $query->where('status', $status);
}
}
Dependency Injection: Laravel’s DI container resolves filters automatically. No manual instantiation needed.
Caching Strategies:
Enable caching in config/filterable.php:
'cache' => [
'enabled' => true,
'driver' => 'redis',
'prefix' => 'filterable_',
],
Use Cacheable trait for automatic caching:
use Filterable\Concerns\Cacheable;
class UserFilter extends Filter
{
use Cacheable;
}
Complex Queries: Chain filters or use closures:
public function activeAndRecent($query)
{
return $query->where('active', true)
->where('created_at', '>', now()->subDays(30));
}
API Integration:
Use Filterable\Concerns\ValidatesRequests to validate input:
use Filterable\Concerns\ValidatesRequests;
class UserFilter extends Filter
{
use ValidatesRequests;
protected $rules = [
'status' => 'in:active,inactive,pending',
];
}
Cache Key Collisions:
getCacheKey() in your filter returns unique keys for different query combinations.md5(request()->getQueryString()), but override if needed:
public function getCacheKey()
{
return md5($this->request->name . $this->request->status);
}
Performance with Large Datasets:
public function apply($query)
{
$this->disableCache();
return parent::apply($query);
}
OptimizesQueries trait to add select() or with() optimizations:
use Filterable\Concerns\OptimizesQueries;
class UserFilter extends Filter
{
use OptimizesQueries;
protected $alwaysSelect = ['id', 'name', 'email'];
}
Request Binding Issues:
snake_case for request keys if your model uses snake_case attributes.Testing Quirks:
$filter = new UserFilter($this->app->make(Request::class));
$filter->setRequest(['name' => 'John']);
getAppliedQuery() to inspect the built query:
$query = $filter->getAppliedQuery();
$this->assertEquals('select * from users where name like ?', $query->toSql());
Dynamic Filter Methods: Dynamically generate filter methods based on request data:
use Filterable\Concerns\DynamicallyFilters;
class UserFilter extends Filter
{
use DynamicallyFilters;
protected $dynamicFilters = ['meta_*'];
}
This will auto-generate methods for meta_age, meta_city, etc.
Rate Limiting:
Combine with Filterable\Concerns\RateLimitsFilters to throttle filter usage:
use Filterable\Concerns\RateLimitsFilters;
class UserFilter extends Filter
{
use RateLimitsFilters;
protected $maxAttempts = 10;
protected $decayMinutes = 1;
}
Custom Validation: Extend validation rules dynamically:
protected function getRules()
{
$rules = $this->defaultRules;
if ($this->request->has('advanced')) {
$rules['status'] = 'required|in:active,inactive,pending,suspended';
}
return $rules;
}
Debugging:
Enable query logging in config/filterable.php:
'debug' => [
'log_queries' => true,
'log_path' => storage_path('logs/filterable.log'),
],
View logs at storage/logs/filterable.log.
Extending the Package:
app/Filterable/Concerns/.$this->app->bind(UserFilter::class, function ($app) {
return new UserFilter($app->make(Request::class));
});
```markdown
## Extension Points
### Customizing Filter Behavior
1. **Override Default Traits**:
Replace `FiltersByRequest` with a custom trait to change how requests are parsed:
```php
namespace App\Filterable\Concerns;
use Filterable\Filter;
trait CustomFiltersByRequest
{
public function handleRequest()
{
// Custom logic to parse request
$this->requestData = $this->parseRequest();
}
}
Add New Concerns:
Create reusable logic in app/Filterable/Concerns/:
namespace App\Filterable\Concerns;
trait LogsFilters
{
public function apply($query)
{
\Log::info('Filter applied: ' . $this->getCacheKey());
return parent::apply($query);
}
}
Extend the Generator:
Modify the Artisan command stubs in vendor/jerome/filterable/src/Filterable/Console/stubs/ or override them in your project’s app/Filterable/Console/stubs/.
GraphQL Support:
Use Filterable\Concerns\FiltersGraphQL to integrate with GraphQL queries:
use Filterable\Concerns\FiltersGraphQL;
class UserFilter extends Filter
{
use FiltersGraphQL;
}
Then resolve filters in your GraphQL resolver:
$filter = app(UserFilter::class, ['input' => $args['filter']]);
return $filter->apply(User::query());
Livewire/Alpine.js: Bind filters to frontend components:
// Alpine.js
document.addEventListener('alpine:init', () => {
Alpine.data('userFilters', () => ({
name: '',
status: '',
apply() {
window.location.search = new URLSearchParams({
name: this.name,
status: this.status
});
}
}));
});
Custom Cache Drivers: Extend the cache logic by binding a custom driver in the service provider:
$this->app->bind('filterable.cache', function ($app) {
return new CustomCacheDriver();
});
Mocking Filters: Use partial mocks for testing:
$filter = $this->partialMock(UserFilter::class, ['name', 'status']);
$filter->shouldReceive('name')->once()->andReturn($query);
Testing Cache Behavior: Mock the cache driver in tests:
$this->app->instance('cache', Mockery::mock('overload', Cache::class));
$this->app['cache']->shouldReceive('get')->
How can I help you explore Laravel packages today?