Ruleset Engine
About 421 wordsAbout 1 min
enginesrulesetoperatorsfiltering
The Ruleset engine turns compact field/operator/value input into Eloquent constraints. The package applies each approved condition automatically, so you do not need to create one PHP method per field.
Choose Ruleset when
- Most filters are direct comparisons.
- The client benefits from a compact query-string format.
- Conditions do not need nested boolean groups.
- Allowed fields and operators are enough to define the public contract.
Request shape
Use the default operator for equality:
GET /api/posts?filter[status]=publishedOr prefix a value with an operator:
GET /api/posts?filter[views]=gte:100&filter[title]=like:%laravel%Ruleset also accepts a one-key operator array such as filter[views][gte]=100. Prefer one format consistently in your public API.
Minimal example
use App\Models\Post;
use Kettasoft\Filterable\Filterable;
$posts = Filterable::for(Post::class, $request)
->using('ruleset')
->setAllowedFields(['status', 'views', 'title'])
->allowedOperators(['eq', 'gte', 'like'])
->latest()
->paginate();Calling paginate() applies the accepted rules before forwarding the terminal operation to Eloquent.
Operator resolution
Without an operator, Ruleset uses the configured default—eq by default.
filter[status]=published → status = published
filter[views]=gte:100 → views >= 100Common built-in operators include eq, neq, gt, gte, lt, lte, like, in, between, null, and their negative variants. See Operator Strategies for the complete list and custom operators.
Relational fields
Authorize relation paths before accepting them:
GET /api/posts?filter[tags][name]=featured$posts = Filterable::for(Post::class, $request)
->using('ruleset')
->allowRelations(['tags' => ['name']])
->paginate();Dot notation and nested request keys normalize to the same relation path. Deep relations can be approved explicitly:
->allowRelations([
'tags.post' => ['status'],
])Strict and permissive modes
Strict mode throws when a request uses a field or operator outside the configured policy:
Filterable::for(Post::class, $request)
->using('ruleset')
->strict()
->setAllowedFields(['status'])
->allowedOperators(['eq'])
->get();Permissive mode skips rejected conditions and keeps their diagnostic payloads available through skipped():
$filterable = Filterable::for(Post::class, $request)
->using('ruleset')
->permissive()
->setAllowedFields(['status']);
$posts = $filterable->get();
$skipped = $filterable->skipped();Common mistakes
- Allowing every model column in a public endpoint.
- Mixing compact
operator:valueand nested operator formats without documenting both. - Allowing relation names without restricting their fields.
- Choosing Ruleset when a condition needs custom joins or domain logic; use Invokable for that case.
Next steps
- Compare request contracts in Choose an Engine.
- Configure operator strategies.
- Add sorting to the filtered query.
