abbasudo / laravel-purity by abbasudo

elegant way to add filter and sort in laravel
24,396
385
4
Package Data
Maintainer Username: abbasudo
Maintainer Contact: llabbasmkhll@gmail.com (abbas mkhzomi)
Package Create Date: 2023-02-20
Package Last Update: 2024-04-24
Home Page: https://abbasudo.github.io/laravel-purity/?utm_source=github&utm_medium=about
Language: PHP
License: MIT
Last Refreshed: 2024-04-28 03:00:02
Package Statistics
Total Downloads: 24,396
Monthly Downloads: 4,815
Daily Downloads: 63
Total Stars: 385
Total Watchers: 4
Total Forks: 30
Total Open Issues: 5

Tests License Latest Unstable Version PHP Version Require StyleCI

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 2 changed filter method 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

Video

youtube

Articles

Installation

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 

Basic Usage

Filters

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.

Sort

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 sort usage.

Advanced Usage

Allowed Fields

By default, purity allows every database column and all model relations to be filtered. 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.

Overwrite Allowed Fields

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.

Rename Fields

To rename fields simply add a value to fields defined in $filterFields and $sortFields arrays:

// App\Models\User

protected $filterFields = [
  'email',
  'mobile' => 'phone',
  'posts'  => 'writing', // relation
];
    
protected $sortFields = [
  'name',
  'mobile' => 'phone',
];

the client should send phone in order to filter by mobile.

Overwrite Renamed Fields

to overwrite renamed fields in the controller you pass renamed fields to filterFields and sortFields.

Post::filterFields(['title', 'created_at' => 'published_date'])->filter()->get();

Post::sortFields([
    'created_at' => 'published_date',
    'updated_at'
  ])->sort()->get();

Note filterFields and sortFields will overwrite fields defined in the model.

Restrict Filters

purity validates allowed filters in the following order of priority:

  • Filters specified in the filters configuration in the configs/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();

Changing Params Source

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();

Livewire

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.transacrion-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

Custom Filters

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'),

Silent Exceptions

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,

Queries and javascript examples

This section is a guide for front-end developers who want to use an API that uses Laravel Purity.

Available Filters

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 | | $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 |

Simple Filtering

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

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

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}`);

Apply Sort

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}`);

Upgrade Guide

Version 2

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

License

Laravel Purity is Licensed under The MIT License (MIT). Please see License File for more information.

Security

If you've found a bug regarding security please mail amkhzomi@gmail.com instead of using the issue tracker.