This package allows you to filter on laravel models
You can choose fields to filtering and customize its data-types, aliases and excepted operators, you can add/customize your request format, and you add new operators or overwrite the existed operators
You can install the package via composer:
composer require asemalalami/laravel-advanced-filter
The package will automatically register its service provider.
You can optionally publish the config file with:
php artisan vendor:publish --provider="AsemAlalami\LaravelAdvancedFilter\AdvancedFilterServiceProvider" --tag="config"
These default config file that will be published: Config File
- use
HasFilter
trait in the model - add fields in the implementation of the abstract function
setupFilter
- call the
filter
scope in your controller
class Order extends Model
{
use HasFilter;
protected $casts = [
'void' => 'boolean',
];
public function channel()
{
return $this->belongsTo(Channel::class);
}
public function orderLines()
{
return $this->hasMany(OrderLine::class);
}
public function setupFilter()
{
$this->addField('void'); // will cast to 'boolean' from the model casts
$this->addField('total')->setDatatype('numeric');
$this->addFields(['source', 'subsource', 'order_date']);
// field from relation
$this->addFields(['channel.created_at' => 'channel_create'])->setDatatype('date');
$this->addField('orderLines.product.sku', 'product_sku');
// field from relation count
$this->addCountField('orderLines');
// custom field (raw sql)
$this->addCustomField('my_total', '(shipping_cost + subtotal)');
// enable general search
$this->addGeneralSearch(['source', 'orderLines.product.sku'], 'startsWith');
}
// customize field filter by custom scope
public function scopeWhereSource(Builder $builder, Field $field, string $operator, $value, $conjunction = 'and')
{
if ($operator == 'Equal') {
return $builder->where(function (Builder $builder) use ($value) {
$builder->where('source', $value)
->orWhere('subsource', $value);
});
}
// default behavior
return $builder->applyOperator($operator, $field, $value, $conjunction);
}
}
...
class OrderController extends Controller
{
public function index()
{
return Order::filter()->paginate(); // you can pass your custom request
}
}
Query format is the shape that you want to send your query(filters) in the request. the package support 3 formats, and you can create a new format.
-
json
(default): the filters will send as json in the requestfilters=[{"field":"email","operator":"equal","value":"abc"}]
-
array
: the filters will send as array in the requestfilters[email][value]=abc&filters[email][operator]=equal
-
separator
: the filters will send as well as in thearray
format, but separated by a separator(^
default)the format sets with a separator symbol
separator:^
filters^email^value=abc&filters^email^operator=equal
set the default query format in the config file
query_format
attribute
- create a new class and extends it from
QueryFormat
:class MyFormat extends QueryFormat
- implement the abstract function
format
that returnsFilterRequest
object - add the class to the config file in
custom_query_format
attribute:'custom_query_format' => MyFormat::class,
Normal Field options:
-
field name is the column name
-
alias is the key that you want to send in the request
-
data-type: by default it set from model
casts
, if you want to set custom data-type, usesetDatatype
-
operators: the field will accept all operators unless you use
setExceptedOperators
to exclude some operators -
a relational field: only set the field name by
.
separatorchannel.name
,channel.type.name
you can define field name by
.
separator, but you want to consider it as a non relational field by passfalse
forinRelation
parameter (used in NoSQL DB or join between tables)$this->addField('channels.name', 'channel_name', false);
-
customize a field query, you can make a scope for the field to customize the filter behavior. scope name must be combined 3 sections :
- scope
- the value of
prefix_scope_function
key in config file (where
is the default) - field name(or relation name) for example
email
public function scopeWhereEmail(Builder $builder, Field $field, string $operator, $value, $conjunction = 'and')
you can customize a relational field by define the scope in the relation model OR define scope by relation name
OrderLine.php public function scopeWherePrice(Builder $builder, Field $field, string $operator, $value, $conjunction = 'and') OR Order.php public function scopeWhereOrderLines(Builder $builder, Field $field, string $operator, $value, $conjunction = 'and')
you can use
applyOperator
function to use the default behavior$builder->applyOperator($operator, $field, $value, $conjunction);
You can add fields to a model by using 4 functions:
addField
(string $field, string $alias = null, ?bool $inRelation = null): by default alias value same as field name value$this->addField('total')->setDatatype('numeric');
addFields
($fields): accept an array of field and aliases:$this->addFields(['created_at' => 'create_date', 'order_date'])->setDatatype('date');
addCountField
(string $relation, string $alias = null, callable $callback = null): add a field from count of relation, use can customize the count query and alias(by default is concat relation name(snake case) and_count
)$this->addCountField('orderLines'); $this->addCountField('orderLines', 'lines_count', function (Builder $builder) { $builder->where('quantity', '>', 1); });
addCustomField
(string $alias, string $sqlRaw, $relation = null): add a field from raw sql query$this->addCustomField('my_total', '(`shipping_cost` + `subtotal`)'); $this->addCustomField('line_subtotal', '(`price` + `quantity`)', 'orderLines'); // inside "orderLines" relation
You can enable general search on some fields, and you can specify the operator (startsWith
is the default operator)
$this->addGeneralSearch(['source', 'orderLines.product.sku'], 'startsWith');
Currently, the package support one conjunction between all fields
and
| or
, default conjunction attribute in the config file default_conjunction
The package has many operators, you can create new operators, and you can customize the operators aliases that you want to send in the request
- Equals (
=
,equals
) - NotEquals (
!=
,notEquals
) - GreaterThan (
>
,greater
) - GreaterThanOrEqual (
>=
,greaterOrEqual
) - LessThan (
<
,less
) - LessThanOrEqual (
<=
,lessOrEqual
) - In (
|
,in
) - NotIn (
!|
,notIn
) - Contains (
*
,contains
) - NotContains (
!*
,notContains
) - StartsWith (
^
,startsWith
) - NotStartsWith (
!^
,notStartsWith
) - EndsWith (
$
,endsWith
) - NotEndsWith (
!$
,notEndsWith
) - Between (
><
,between
)
- create a new class and extends it from
Operator
:class MyOperator extends Operator
- implement the abstract function
apply
andgetSqlOperator
(used as a default sql operator for count and custom field) - add the class in the config file in
custom_operators
attribute:'custom_operators' => [MyOperator::class => ['my-op', '*']],
- boolean
- date
- datetime
- numeric
- string