indexzer0/eloquent-filtering
Define allowed filters on your Eloquent models and apply them from simple arrays or request data—no custom query logic. Supports complex, type-based filtering for APIs and dashboards on Laravel 10+ / PHP 8.2+.
Pivot filters can be specified in two ways:
Pivot, MorphPivot).Model).If you are utilising Custom Intermediate Table Models, you may define filters on that model.
Filters defined on this model will be applied to the intermediate table.
/*
* User Model
*/
class User extends Model implements IsFilterable
{
use Filterable;
public function roles(): BelongsToMany
{
return $this->belongsToMany(Role::class)->using(RoleUser::class);
}
public function allowedFilters(): AllowedFilterList
{
return Filter::only(
Filter::relation('roles', [FilterType::HAS])->includeRelationFields(),
);
}
}
/*
* Role Model
*/
class Role extends Model implements IsFilterable
{
use Filterable;
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class)->using(RoleUser::class);
}
public function allowedFilters(): AllowedFilterList
{
return Filter::only(
Filter::relation('users', [FilterType::HAS])->includeRelationFields(),
);
}
}
/*
* RoleUser Pivot Model
*/
class RoleUser extends Pivot implements IsFilterable
{
use Filterable;
public function allowedFilters(): AllowedFilterList
{
return Filter::only(
Filter::field('assigned_by', [FilterType::EQUAL]),
);
}
}
/*
* Applying `assigned_by` pivot filter
* within `roles` `$has` relation filter.
*/
User::filter([
[
'type' => '$has',
'target' => 'roles',
'value' => [
[
'type' => '$eq',
'target' => 'assigned_by',
'value' => 'admin',
]
]
]
]);
/*
* Applying `assigned_by` pivot filter
* within `users` `$has` relation filter.
*/
Role::filter([
[
'type' => '$has',
'target' => 'users',
'value' => [
[
'type' => '$eq',
'target' => 'assigned_by',
'value' => 'admin',
]
]
]
]);
Sometimes you may only want the pivot filter to be allowed when filtering by relationship one way but not the other way.
For example:
User $has Role.Role $has User.To achieve this you can specify the allowed "from" model for the pivot filter.
/*
* RoleUser Pivot Model
*/
class RoleUser extends Pivot implements IsFilterable
{
use Filterable;
public function allowedFilters(): AllowedFilterList
{
return Filter::only(
Filter::field('assigned_by', [FilterType::EQUAL])
->pivot(User::class),
);
}
}
If you are NOT utilising Custom Intermediate Table Models, you may define pivot filters on your main models.
Filter::field() filters can be marked as pivot filters if you want the filter to be applied to a column on the intermediate table linking the models.
You must specify the parent model fqcn.
public function allowedFilters(): AllowedFilterList
{
return Filter::only(
Filter::field('tagged_by', [FilterType::EQUAL])
->pivot(Post::class),
);
}
In the below example of class Post and class Tag.
allowedFilters method of both classes.posts or tags relationship.class Post extends Model implements IsFilterable
{
use Filterable;
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class);
}
public function allowedFilters(): AllowedFilterList
{
return Filter::only(
Filter::field('tagged_by', [FilterType::EQUAL])->pivot(Tag::class),
Filter::relation('tags', [FilterType::HAS])->includeRelationFields()
);
}
}
class Tag extends Model implements IsFilterable
{
use Filterable;
public function posts(): BelongsToMany
{
return $this->belongsToMany(Post::class);
}
public function allowedFilters(): AllowedFilterList
{
return Filter::only(
Filter::field('tagged_by', [FilterType::EQUAL])->pivot(Post::class),
Filter::relation('posts', [FilterType::HAS])->includeRelationFields()
);
}
}
With the models setup as described above. The following filters are allowed.
/*
* Applying `tagged_by` pivot filter
* within `tags` `$has` relation filter.
*/
Post::filter([
[
'type' => '$has',
'target' => 'tags',
'value' => [
[
'type' => '$eq',
'target' => 'tagged_by',
'value' => 'admin',
]
]
]
]);
/*
* Applying `tagged_by` pivot filter
* within `posts` `$has` relation filter.
*/
Tag::filter([
[
'type' => '$has',
'target' => 'posts',
'value' => [
[
'type' => '$eq',
'target' => 'tagged_by',
'value' => 'admin',
]
]
]
]);
With the models setup as described above. The following filters are denied.
/*
* Applying `tagged_by` pivot filter
* when not in context of `tags` `$has` relation filter.
* throws DeniedFilterException.
*/
Post::filter([
[
'type' => '$eq',
'target' => 'tagged_by',
'value' => 'admin',
]
]);
/*
* Applying `tagged_by` pivot filter
* when not in context of `posts` `$has` relation filter.
* throws DeniedFilterException.
*/
Tag::filter([
[
'type' => '$eq',
'target' => 'tagged_by',
'value' => 'admin',
]
]);
/*
* Applying `tagged_by` pivot filter
* when in context of another BelongsToMany relation
* but User::class not defined in the ->pivot() method in the tags model.
* throws DeniedFilterException.
*/
User::filter([
[
'type' => '$has',
'target' => 'tags',
'value' => [
[
'type' => '$eq',
'target' => 'tagged_by',
'value' => 'admin',
]
]
]
]);
When defining a pivot filter for MorphToMany relations, you can specify a list of models in the ->pivot() method.
class Epic extends Model implements IsFilterable
{
// ...
public function allowedFilters(): AllowedFilterList
{
return Filter::only(
Filter::relation('labels', [FilterType::HAS])->includeRelationFields()
);
}
}
class Issue extends Model implements IsFilterable
{
// ...
public function allowedFilters(): AllowedFilterList
{
return Filter::only(
Filter::relation('labels', [FilterType::HAS])->includeRelationFields()
);
}
}
class Label extends Model implements IsFilterable
{
// ...
public function allowedFilters(): AllowedFilterList
{
return Filter::only(
Filter::field('labeled_by', [FilterType::EQUAL])
->pivot(Epic::class, Issue::class)
);
}
}
How can I help you explore Laravel packages today?