freebuu / laravel-filterable
Model filters for your index requests
Installs: 5
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
Type:package
Requires
- php: ^8.1
- illuminate/database: ^9.0 | ^10.0 | ^11.0
- illuminate/http: ^9.0 | ^10.0 | ^11.0
- illuminate/support: ^9.0 | ^10.0 | ^11.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.54
- orchestra/testbench: ^7.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^9.6
This package is auto-updated.
Last update: 2024-12-27 12:19:27 UTC
README
Simple filter and paginate index data. Main idea - KISS.
Example query:
?search_description=some%20text&sort_id=desc&where_publisher_id=1,23&where_has_groups__id=30,40&limit=10&offset=20
In code this query reflects:
- search_description=some%20text -
$builder->where('description', 'like', '%some%text%')
- sort_id=desc -
$builder->sortBy('id', 'desc')
- where_publisher_id=1,23 -
$builder->whereIn('publisher_id', [1,23])
- where_has_groups__id=30,40 -
$builder->whereHas('groups', fn($builder) => $builder->whereIn('id', [30,40]))
- limit=10 -
$builder->limit(10)
- offset=20 -
$builder->offset(20)
Installation
Requires laravel >= 9 and php ^8.1
composer require freebuu/laravel-filterable
Basic usage (pagination only)
Add HasRequestFilter trait to Model - that's all.
class PostIndexController { public function __invoke(Request $request) { $data = Post::requestFilter()->setResource(PostResource::class); return response()->json($data); } }
Example result for query /api/posts/?limit=25&offset=10
{ "meta": { // object with pagination data "limit": 25, "offset": 10, "total": 2 }, "data": [ //array with model data, wrapped in `PostResource` (of course you can not use resource and simply output collection or pass collection to view) { "id": "1", "title": "post title" }, { "id": "2", "title": "another post title" } ] }
Filtration
For filtration, you need to create filter class for each model. Filter class must extend AbstractFilter. Best place for these classes is App\Http\Filters
.
In method getFilterableFields()
you specify which fields can be filtered in each filter case.
HINT - always add default
state because filter cases may be supplemented.
class PostFilter extends AbstractFilter { protected function getFilterableFields(FilterCaseEnum $case): array { return match ($case) { FilterCaseEnum::WHERE => ['publisher_id'], FilterCaseEnum::SORT => ['id'], FilterCaseEnum::WHERE_HAS => ['groups' => ['id']] default => [] }; } }
To set this filter for Model
- overwrite requestFilterClass()
method
class Post extends Model { use HasRequestFilter; public function requestFilterClass(): string { return \App\Http\Filters\PostFilter::class; } }
Filter case
Filterable query params contains four parts separated with _
. Let's see example with where_has_groups__id=30,40
- $case - where_has
- $field - groups
- $fieldValue - id (optional, mandatory only with where_has)
- $value - 30,40
In code, they are presented as FilterCaseEnum.php and they work like this
- FROM
- Accepts only int
$builder->where($field, '>=', $value)
- TO
- Accepts only int
$builder->where($field, '<=', $value)
-
- SORT - sorting
- Accepts only
asc
,desc
$builder->sortBy($field, $value)
- Accepts only
- SEARCH - search with
like
operator- Accept string with spaces. Add
%
at start, end and instead of all spaces $builder->where($field', 'like', $value)
- Accept string with spaces. Add
- START_WITH - all strings starts with passed value
- Accept string without spaces. Add
%
at end of value $builder->where($field', 'like', $value)
- Accept string without spaces. Add
- WHERE_HAS - filter by relation with array of values
- Accept comma separated array of values.
- For this filter
fieldValue
fields must be set (see in example inPostFilter
) $builder->whereHas($field, fn($builder) => $builder->whereIn($fieldValue, $value))
- WHERE - filter by array of values
- Accept comma separated array of values.
$builder->whereIn($fieldValue, $value)
- FILTER - uses for custom filters, see below
Custom filters
In filter class you can make custom filter by creating a method like filterCustom
- it must begin with filter
. Then yoy can use it in query like ?filter_custom=123
HINT - you can use fieldValue
here like ?filter_custom__alias=123
- it pass as third parameter in filter method.
class PostFilter extends AbstractFilter { public function filterCustom(Builder $builder, mixed $value, mixed $fieldValue): void { //you have request instance here if($this->request->query('something')){ return; } //$value will be 123 //$fieldValue will be alias (or can be null) $builder->where('some_field', $value)->where($fieldValue, '432') } }
Advanced usage
Limit
For security reasons, the limit
field is set to a maximum value. If the request specifies a value greater, it will be reset to the default value.
- Default it set to
30
- You can override this value in Filter class - overwrite the
maxLimit
property. - Or you can override it system-wide in
AppServiceProvider
class AppServiceProvider extends ServiceProvider { public function register() { AbstractFilter::$defaultMaxLimit = 50; } }
Resource
To set up resource - just pass resource class like here Post::requestFilter()->response(PostResource::class)
.
Query callbacks
Sometimes you need to set up query condition situational - e.g. filter only for auth user
Post::requestFilter() ->addQueryCallback(fn (Builder $builder) => $builder->where('author_id', auth()->id())) ->response(ResourceClass::class);