Note If you are a front-end developer and want to make queries in an API that uses this package head to the queries section
Note Version 3 changed
$filterFields
read more at upgrade guide section
Laravel Purity is an elegant and efficient filtering and sorting package for Laravel, designed to simplify complex data filtering and sorting logic for eloquent queries. By simply adding filter()
to your Eloquent query, you can add the ability for frontend users to apply filters based on URL query string parameters like a breeze.
Features :
- Livewire support (added in v2)
- Rename and restrict fields (added in v2)
- Various filter methods
- Simple installation and usage
- Filter by relation columns
- Custom filters
- Multi-column sort
Laravel Purity is not only developer-friendly but also front-end developer-friendly. Frontend developers can effortlessly use filtering and sorting of the APIs by using the popular JavaScript qs package.
The way this package handles filters is inspired by strapi's filter and sort functionality.
- Tutorials
- Installation
- Basic Usage
- Advanced Usage
- Queries and javascript examples
- Upgrade Guide
- License
- Security
- Filter API Responses with Laravel Purity
- Filter and Sort in Laravel
- Filter better with Laravel Purity
- The correct way of adding filters to Laravel
- Add filter to your laravel app
- Enable filtering queries in your laravel 10 app with ease
Install the package via composer by this command:
composer require abbasudo/laravel-purity
Get configs (configs/purity.php
) file to customize the package's behavior by this command:
php artisan vendor:publish --tag=purity
Add Filterable
trait to your model to get filters functionalities.
use Abbasudo\Purity\Traits\Filterable;
class Post extends Model
{
use Filterable;
//
}
Now add filter()
to your model eloquent query in the controller.
use App\Models\Post;
class PostController extends Controller
{
public function index()
{
return Post::filter()->get();
}
}
By default, it gives access to all filters available. here is the list of available filters. if you want to explicitly specify which filters to use in this call head to restrict filters section.
Add Sortable
trait to your model to get sorts functionalities.
use Abbasudo\Purity\Traits\Sortable;
class Post extends Model
{
use Sortable;
//
}
Now add sort()
to your eloquent query in the controller.
use App\Models\Post;
class PostController extends Controller
{
public function index()
{
return Post::sort()->get();
}
}
Now sort can be applied as instructed in apply sort.
By default, purity allows every database column and all model relations (that have a defined return type) to be filtered.
// App\Models\User
public function posts(): Illuminate\Database\Eloquent\Relations\HasMany // This is mandatory
{
return $this->hasMany(Post::class);
}
you can overwrite the allowed columns as follows:
// App\Models\User
protected $filterFields = [
'email',
'mobile',
'posts', // relation
];
protected $sortFields = [
'name',
'mobile',
];
any field other than email, mobile, or posts will be rejected when filtering.
to overwrite allowed fields in the controller add filterFields
or sortFields
before calling filter or sort method.
Post::filterFields('title', 'created_at')->filter()->get();
Post::sortFields('created_at', 'updated_at')->sort()->get();
Note filterFields and sortFields will overwrite fields defined in the model.
To rename filter fields simply add a value to fields defined in $renamedFilterFields
.
// App\Models\User
// ?filter[phone][$eq]=0000000000
protected $renamedFilterFields = [
'mobile' => 'phone', // Actual database column is mobile
'posts' => 'writing', // actual relation is posts
];
The client should send phone in order to filter by mobile column in database.
To rename sort fields simply add a value to defined in $sortFields
// App\Models\User
// ?sort=phone
protected $sortFields = [
'name',
'mobile' => 'phone', // Actual database column is mobile
];
The client should send phone in order to sort by mobile column in database.
To overwrite renamed fields in the controller you pass renamed fields to rebamedFilterFields
and sortFields
.
Post::renamedFilterFields(['created_at' => 'published_date'])->filter()->get();
Post::sortFields([
'created_at' => 'published_date',
'updated_at'
])->sort()->get();
Note SortFields will overwrite fields defined in the model.
purity validates allowed filters in the following order of priority:
- Filters specified in the
filters
configuration in theconfigs/purity.php
file.
// configs/purity.php
'filters' => [
EqualFilter::class,
InFilter::class,
],
- Filters declared in the
$filters
variable in the model.
note that, $filters will overwrite configs filters.
// App\Models\Post
private array $filters = [
'$eq',
'$in',
];
// or
private array $filters = [
EqualFilter::class,
InFilter::class,
];
- Filters passed as an array to the
filterBy()
function.
note that, filterBy
will overwrite all other defined filters.
Post::filterBy('$eq', '$in')->filter()->get();
// or
Post::filterBy(EqualFilter::class, InFilter::class)->filter()->get();
There are three available options for your convenience. They take priority respectively.
- Option 1 : Define restricted filters inside
$filterFields
property, as shown below
$filterFields = [
'title' => ['$eq'], // title will be limited to the eq operator
'title' => '$eq', // works only for one restricted operator
'title:$eq', // same as above
'title', // this won't be restricted to any operator
];
The drawback here is that you have to define all the allowed fields, regardless of any restrictions fields.
- Option 2 : Define them inside
$restrictedFilters
property
$restrictedFields = [
'title' => ['$eq', '$eq'], // title will be limited to the eq operator
'title:$eq,$in' // same as above
'title' // this won't be restricted to any operator
];
- Option 3 : Finally, you can set it on the Eloquent builder, which takes the highest priority (overwrite all the above options)
Post::restrictedFilters(['title' => ['$eq']])->filter()->get();
Note
All field-restricted filter operations are respected to filters defined in $filter (here). This means you are not allowed to restrict a field operation that is not permitted in restricted fields.
$filters = ['$eq'];
$restrictedFilters = ['title' => ['$eqc']] // This won't work
The following relationship types are supported.
- One to One
- One to Many
- Many to Many
Return type of the relationship mandatory as below in order to sort by relationships.
use Illuminate\Database\Eloquent\Relations\HasMany;
class Post extends Model
{
use Sortable;
public function tags(): HasMany // This is mandatory
{
return $this->hasMany(Tag::class);
}
}
By Default, purity gets params from filters index in query params, overwrite this by passing params directly to filter or sort functions:
Post::filter($params)->get();
Post::filter([
'title' => ['$eq' => 'good post']
])->get();
Post::sort([
'title',
'id:desc'
])->get();
to add filter to your livewire app, first define $filters
variable in your component and pass it to filter or sort method:
// component
#[Url]
public $filters = [
'title' => [],
];
public function render()
{
$transactions = Transaction::filter($this->filters)->get();
return view('livewire.transaction-table',compact('transactions'));
}
then bind the variable in your blade template.
<!-- in blade template -->
<input type="text" wire:model.live="filters.title.$eq" placeholder="title" />
read more in livewire docs
Create a custom filter class by this command:
php artisan make:filter EqualFilter
this will generate a filter class in Filters
directory. By default, all classes defined in Filters
directory are loaded into the package. you can change scan folder location in purity config file.
// configs/purity.php
'custom_filters_location' => app_path('Filters'),
By default, purity silences its own exceptions. to change that behavior change the silent
index to false
in the config file.
// configs/purity.php
'silent' => false,
When sorting a column that contains null values, it's typically preferred to have those values appear last, regardless of the sorting direction. You can enable this feature in the configuration as follows:
// configs/purity.php
null_last => true;
This section is a guide for front-end developers who want to use an API that uses Laravel Purity.
Queries can accept a filters parameter with the following syntax:
GET /api/posts?filters[field][operator]=value
By Default the following operators are available:
Operator | Description |
---|---|
$eq |
Equal |
$eqc |
Equal (case-sensitive) |
$ne |
Not equal |
$lt |
Less than |
$lte |
Less than or equal to |
$gt |
Greater than |
$gte |
Greater than or equal to |
$in |
Included in an array |
$notIn |
Not included in an array |
$contains |
Contains |
$notContains |
Does not contain |
$containsc |
Contains (case-sensitive) |
$notContainsc |
Does not contain (case-sensitive) |
$null |
Is null |
$notNull |
Is not null |
$between |
Is between |
$notBetween |
Is not between |
$startsWith |
Starts with |
$startsWithc |
Starts with (case-sensitive) |
$endsWith |
Ends with |
$endsWithc |
Ends with (case-sensitive) |
$or |
Joins the filters in an "or" expression |
$and |
Joins the filters in an "and" expression |
Tip In javascript use qs directly to generate complex queries instead of creating them manually. Examples in this documentation showcase how you can use
qs
.
Find users having 'John' as their first name
GET /api/users?filters[name][$eq]=John
const qs = require('qs');
const query = qs.stringify({
filters: {
username: {
$eq: 'John',
},
},
}, {
encodeValuesOnly: true, // prettify URL
});
await request(`/api/users?${query}`);
Find multiple restaurants with ids 3, 6, 8
GET /api/restaurants?filters[id][$in][0]=3&filters[id][$in][1]=6&filters[id][$in][2]=8
const qs = require('qs');
const query = qs.stringify({
filters: {
id: {
$in: [3, 6, 8],
},
},
}, {
encodeValuesOnly: true, // prettify URL
});
await request(`/api/restaurants?${query}`);
Complex filtering is combining multiple filters using advanced methods such as combining $and
& $or
. This allows for more flexibility to request exactly the data needed.
Find books with 2 possible dates and a specific author.
GET /api/books?filters[$or][0][date][$eq]=2020-01-01&filters[$or][1][date][$eq]=2020-01-02&filters[author][name][$eq]=Kai%20doe
const qs = require('qs');
const query = qs.stringify({
filters: {
$or: [
{
date: {
$eq: '2020-01-01',
},
},
{
date: {
$eq: '2020-01-02',
},
},
],
author: {
name: {
$eq: 'Kai doe',
},
},
},
}, {
encodeValuesOnly: true, // prettify URL
});
await request(`/api/books?${query}`);
Deep filtering is filtering on a relation's fields.
Find restaurants owned by a chef who belongs to a 5-star restaurant
GET /api/restaurants?filters[chef][restaurants][stars][$eq]=5
const qs = require('qs');
const query = qs.stringify({
filters: {
chef: {
restaurants: {
stars: {
$eq: 5,
},
},
},
},
}, {
encodeValuesOnly: true, // prettify URL
});
await request(`/api/restaurants?${query}`);
Queries can accept a sort parameter that allows sorting on one or multiple fields with the following syntax's:
GET /api/:pluralApiId?sort=value
to sort on 1 field
GET /api/:pluralApiId?sort[0]=value1&sort[1]=value2
to sort on multiple fields (e.g. on 2 fields)
The sorting order can be defined as:
:asc
for ascending order (default order, can be omitted):desc
for descending order.
Usage Examples
Sort using 2 fields
GET /api/articles?sort[0]=title&sort[1]=slug
const qs = require('qs');
const query = qs.stringify({
sort: ['title', 'slug'],
}, {
encodeValuesOnly: true, // prettify URL
});
await request(`/api/articles?${query}`);
Sort using 2 fields and set the order
GET /api/articles?sort[0]=title%3Aasc&sort[1]=slug%3Adesc
const qs = require('qs');
const query = qs.stringify({
sort: ['title:asc', 'slug:desc'],
}, {
encodeValuesOnly: true, // prettify URL
});
await request(`/api/articles?${query}`);
All the usages of basic sorting are applicable. Use dot(.) notation to apply relationship in the following format.
?sort=[relationship name].[relationship column]:[sort direction]
Usage Examples
The query below sorts posts by their tag name in ascending order (default sort direction). Direction is not mandatory when sorted by ascending order.
GET /api/posts?sort=tags.name:asc
const qs = require('qs');
const query = qs.stringify({
sort: ['tags.name:asc'],
}, {
encodeValuesOnly: true, // prettify URL
});
await request(`/api/posts?${query}`);
Note
Sorting by nested relationships is not supported by the package as of now.
changed how $filterFields
array works. it no longer renames fields, instead, it restricts filters that are accepted by the field as mentioned in the Restrict filters section.
to rename fields refer to Rename fields. sortFields
However, didnt change.
changed filter function arguments. filter function no longer accepts filter methods, instead, filter function now accepts filter source as mentioned in Custom Filters section. to specify allowed filter methods use filterBy as mentioned in Restrict Filters
Laravel Purity is Licensed under The MIT License (MIT). Please see License File for more information.
If you've found a bug regarding security please mail [email protected] instead of using the issue tracker.