diff --git a/src/AllowedFilter.php b/src/AllowedFilter.php index fcdbd106..1db6395e 100644 --- a/src/AllowedFilter.php +++ b/src/AllowedFilter.php @@ -2,6 +2,7 @@ namespace Spatie\QueryBuilder; +use Closure; use Illuminate\Support\Collection; use Spatie\QueryBuilder\Enums\FilterOperator; use Spatie\QueryBuilder\Filters\Filter; @@ -27,6 +28,8 @@ class AllowedFilter protected bool $nullable = false; + protected ?Closure $condition = null; + public function __construct( protected string $name, protected Filter $filterClass, @@ -39,6 +42,10 @@ public function __construct( public function filter(QueryBuilder $query, $value): void { + if ($this->condition && ! value($this->condition, $this)) { + return; + } + $valueToFilter = $this->resolveValueForFiltering($value); if (! $this->nullable && is_null($valueToFilter)) { @@ -204,4 +211,11 @@ protected function resolveValueForFiltering($value) return ! $this->ignored->contains($value) ? $value : null; } + + public function when(Closure $callback): static + { + $this->condition = $callback; + + return $this; + } } diff --git a/tests/FilterTest.php b/tests/FilterTest.php index 75d5042a..6483a61c 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -894,3 +894,33 @@ public function __invoke(Builder $query, $value, string $property): Builder expect($results)->toHaveCount(2); }); + +it('can filter models by partial property when the condition return true', function () { + $models = createQueryFromFilterRequest([ + 'name' => $this->models->first()->name, + ]) + ->allowedFilters(AllowedFilter::partial('name')->when(fn () => true)) + ->get(); + + expect($models)->toHaveCount(1); +}); + +it('cannot filter models by partial property when the condition return false', function () { + $models = createQueryFromFilterRequest([ + 'name' => $this->models->first()->name, + ]) + ->allowedFilters(AllowedFilter::partial('name')->when(fn () => false)) + ->get(); + + expect($models)->toHaveCount(5); +}); + +it('can filter only trashed passing the class as a parameter to evaluate the condition', function () { + $models = createQueryFromFilterRequest([ + 'name' => $this->models->first()->name, + ]) + ->allowedFilters(AllowedFilter::partial('name')->when(fn (AllowedFilter $filter) => $filter->getName() === 'name')) + ->get(); + + expect($models)->toHaveCount(1); +}); diff --git a/tests/FiltersTrashedTest.php b/tests/FiltersTrashedTest.php index 517538e8..1623d3c3 100644 --- a/tests/FiltersTrashedTest.php +++ b/tests/FiltersTrashedTest.php @@ -51,3 +51,33 @@ expect($models)->toHaveCount(3); }); + +it('can filter only trashed when the condition return true', function () { + $models = createQueryFromFilterRequest([ + 'trashed' => 'only', + ], SoftDeleteModel::class) + ->allowedFilters(AllowedFilter::trashed()->when(fn () => true)) + ->get(); + + expect($models)->toHaveCount(1); +}); + +it('cannot filter only trashed when the condition return false', function () { + $models = createQueryFromFilterRequest([ + 'trashed' => 'only', + ], SoftDeleteModel::class) + ->allowedFilters(AllowedFilter::trashed()->when(fn () => false)) + ->get(); + + expect($models)->toHaveCount(2); +}); + +it('can filter only trashed passing the class as a parameter to evaluate the condition', function () { + $models = createQueryFromFilterRequest([ + 'trashed' => 'only', + ], SoftDeleteModel::class) + ->allowedFilters(AllowedFilter::trashed()->when(fn (AllowedFilter $filter) => $filter->getName() === 'trashed')) + ->get(); + + expect($models)->toHaveCount(1); +});