Overview
Pivot filters can be specified in two ways:
- on Custom Intermediate Table Models (
Pivot
, MorphPivot
).
- on your main models (
Model
).
This is the recommended way
- Define the allowed pivot filters once in the Custom Intermediate Table Model.
- Not required to call
->pivot()
.
- Caveat: Must call
->includeRelationFields()
on Filter::relation()
.
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',
]
]
]
]);
Restricting Allowed Pivot Filters.
Sometimes you may only want the pivot filter to be allowed when filtering by relationship one way but not the other way.
For example:
- Allowed when filtering
User
$has
Role
.
- Denied when filtering
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),
);
}
BelongsToMany
In the below example of class Post
and class Tag
.
- The pivot filter is specified in the
allowedFilters
method of both classes.
- The pivot filter can only be used when in the context of the
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()
);
}
}
Allowed
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',
]
]
]
]);
DeniedFilterException
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',
]
]
]
]);
MorphToMany (Polymorphic)
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)
);
}
}