From 47b1a58ae32a2e02f20713019f5aa57a56de7642 Mon Sep 17 00:00:00 2001 From: btxtiger Date: Tue, 18 Aug 2020 17:02:43 +0200 Subject: [PATCH] Individual array delimiter for a single filter #450 (#451) * Gitignore IntelliJ Workspace * Individual array delimiter for a single filter #450 * Add tests + set delimiter arg as optional #450 * Fix style CI * Fix style CI * Fix style CI --- .gitignore | 1 + docs/advanced-usage/multi-value-delimiter.md | 15 +++ src/AllowedFilter.php | 37 +++--- src/QueryBuilderRequest.php | 113 ++++++++++++++----- tests/FilterTest.php | 34 ++++++ 5 files changed, 157 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index c82210c2..aecf84f8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ vendor .php_cs.cache coverage .phpunit.result.cache +/.idea diff --git a/docs/advanced-usage/multi-value-delimiter.md b/docs/advanced-usage/multi-value-delimiter.md index 6d2538cb..844baacd 100644 --- a/docs/advanced-usage/multi-value-delimiter.md +++ b/docs/advanced-usage/multi-value-delimiter.md @@ -36,3 +36,18 @@ public function handle($request, $next) { return $next($request); } ``` + +You can also set the delimiter for each feature individually: +```php +QueryBuilderRequest::setIncludesArrayValueDelimiter(';'); // Includes +QueryBuilderRequest::setAppendsArrayValueDelimiter(';'); // Appends +QueryBuilderRequest::setFieldsArrayValueDelimiter(';'); // Fields +QueryBuilderRequest::setSortsArrayValueDelimiter(';'); // Sorts +QueryBuilderRequest::setFilterArrayValueDelimiter(';'); // Filter +``` + +You can override the default delimiter for single filters: +```php +// GET /api/endpoint?filter[id]=h4S4MG3(+>azv4z/I,>XZII/Q1On +AllowedFilter::exact('id', 'ref_id', true, ';'); +``` diff --git a/src/AllowedFilter.php b/src/AllowedFilter.php index 8a3038c4..8d2d7dbb 100644 --- a/src/AllowedFilter.php +++ b/src/AllowedFilter.php @@ -49,29 +49,38 @@ public function filter(QueryBuilder $query, $value) ($this->filterClass)($query->getEloquentBuilder(), $valueToFilter, $this->internalName); } - public static function exact( - string $name, - ?string $internalName = null, - bool $addRelationConstraint = true - ): self { + public static function setFilterArrayValueDelimiter(string $delimiter = null): void + { + if (isset($delimiter)) { + QueryBuilderRequest::setFilterArrayValueDelimiter($delimiter); + } + } + + public static function exact(string $name, ?string $internalName = null, bool $addRelationConstraint = true, string $arrayValueDelimiter = null): self + { + static::setFilterArrayValueDelimiter($arrayValueDelimiter); + return new static($name, new FiltersExact($addRelationConstraint), $internalName); } - public static function partial( - string $name, - $internalName = null, - bool $addRelationConstraint = true - ): self { + public static function partial(string $name, $internalName = null, bool $addRelationConstraint = true, string $arrayValueDelimiter = null): self + { + static::setFilterArrayValueDelimiter($arrayValueDelimiter); + return new static($name, new FiltersPartial($addRelationConstraint), $internalName); } - public static function scope(string $name, $internalName = null): self + public static function scope(string $name, $internalName = null, string $arrayValueDelimiter = null): self { + static::setFilterArrayValueDelimiter($arrayValueDelimiter); + return new static($name, new FiltersScope(), $internalName); } - public static function callback(string $name, $callback, $internalName = null): self + public static function callback(string $name, $callback, $internalName = null, string $arrayValueDelimiter = null): self { + static::setFilterArrayValueDelimiter($arrayValueDelimiter); + return new static($name, new FiltersCallback($callback), $internalName); } @@ -80,8 +89,10 @@ public static function trashed(string $name = 'trashed', $internalName = null): return new static($name, new FiltersTrashed(), $internalName); } - public static function custom(string $name, Filter $filterClass, $internalName = null): self + public static function custom(string $name, Filter $filterClass, $internalName = null, string $arrayValueDelimiter = null): self { + static::setFilterArrayValueDelimiter($arrayValueDelimiter); + return new static($name, $filterClass, $internalName); } diff --git a/src/QueryBuilderRequest.php b/src/QueryBuilderRequest.php index abca8c96..2020654d 100644 --- a/src/QueryBuilderRequest.php +++ b/src/QueryBuilderRequest.php @@ -8,16 +8,19 @@ class QueryBuilderRequest extends Request { - private static $arrayValueDelimiter = ','; + private static $includesArrayValueDelimiter = ','; + private static $appendsArrayValueDelimiter = ','; + private static $fieldsArrayValueDelimiter = ','; + private static $sortsArrayValueDelimiter = ','; + private static $filterArrayValueDelimiter = ','; public static function setArrayValueDelimiter(string $delimiter): void { - static::$arrayValueDelimiter = $delimiter; - } - - public static function getArrayValueDelimiter(): string - { - return static::$arrayValueDelimiter; + static::$filterArrayValueDelimiter = $delimiter; + static::$includesArrayValueDelimiter = $delimiter; + static::$appendsArrayValueDelimiter = $delimiter; + static::$fieldsArrayValueDelimiter = $delimiter; + static::$sortsArrayValueDelimiter = $delimiter; } public static function fromRequest(Request $request): self @@ -32,7 +35,7 @@ public function includes(): Collection $includeParts = $this->query($includeParameterName); if (! is_array($includeParts)) { - $includeParts = explode(static::getArrayValueDelimiter(), $this->query($includeParameterName)); + $includeParts = explode(static::getIncludesArrayValueDelimiter(), $this->query($includeParameterName)); } return collect($includeParts) @@ -47,29 +50,12 @@ public function appends(): Collection $appendParts = $this->query($appendParameterName); if (! is_array($appendParts)) { - $appendParts = explode(static::getArrayValueDelimiter(), strtolower($appendParts)); + $appendParts = explode(static::getAppendsArrayValueDelimiter(), strtolower($appendParts)); } return collect($appendParts)->filter(); } - public function filters(): Collection - { - $filterParameterName = config('query-builder.parameters.filter'); - - $filterParts = $this->query($filterParameterName, []); - - if (is_string($filterParts)) { - return collect(); - } - - $filters = collect($filterParts); - - return $filters->map(function ($value) { - return $this->getFilterValue($value); - }); - } - public function fields(): Collection { $fieldsParameterName = config('query-builder.parameters.fields'); @@ -81,7 +67,7 @@ public function fields(): Collection } return $fieldsPerTable->map(function ($fields) { - return explode(static::getArrayValueDelimiter(), $fields); + return explode(static::getFieldsArrayValueDelimiter(), $fields); }); } @@ -92,12 +78,29 @@ public function sorts(): Collection $sortParts = $this->query($sortParameterName); if (is_string($sortParts)) { - $sortParts = explode(static::getArrayValueDelimiter(), $sortParts); + $sortParts = explode(static::getSortsArrayValueDelimiter(), $sortParts); } return collect($sortParts)->filter(); } + public function filters(): Collection + { + $filterParameterName = config('query-builder.parameters.filter'); + + $filterParts = $this->query($filterParameterName, []); + + if (is_string($filterParts)) { + return collect(); + } + + $filters = collect($filterParts); + + return $filters->map(function ($value) { + return $this->getFilterValue($value); + }); + } + /** * @param $value * @@ -111,8 +114,8 @@ protected function getFilterValue($value) })->all(); } - if (Str::contains($value, static::getArrayValueDelimiter())) { - return explode(static::getArrayValueDelimiter(), $value); + if (Str::contains($value, static::getFilterArrayValueDelimiter())) { + return explode(static::getFilterArrayValueDelimiter(), $value); } if ($value === 'true') { @@ -125,4 +128,54 @@ protected function getFilterValue($value) return $value; } + + public static function setIncludesArrayValueDelimiter(string $includesArrayValueDelimiter): void + { + static::$includesArrayValueDelimiter = $includesArrayValueDelimiter; + } + + public static function setAppendsArrayValueDelimiter(string $appendsArrayValueDelimiter): void + { + static::$appendsArrayValueDelimiter = $appendsArrayValueDelimiter; + } + + public static function setFieldsArrayValueDelimiter(string $fieldsArrayValueDelimiter): void + { + static::$fieldsArrayValueDelimiter = $fieldsArrayValueDelimiter; + } + + public static function setSortsArrayValueDelimiter(string $sortsArrayValueDelimiter): void + { + static::$sortsArrayValueDelimiter = $sortsArrayValueDelimiter; + } + + public static function setFilterArrayValueDelimiter(string $filterArrayValueDelimiter): void + { + static::$filterArrayValueDelimiter = $filterArrayValueDelimiter; + } + + public static function getIncludesArrayValueDelimiter(): string + { + return static::$includesArrayValueDelimiter; + } + + public static function getAppendsArrayValueDelimiter(): string + { + return static::$appendsArrayValueDelimiter; + } + + public static function getFieldsArrayValueDelimiter(): string + { + return static::$fieldsArrayValueDelimiter; + } + + public static function getSortsArrayValueDelimiter(): string + { + return static::$sortsArrayValueDelimiter; + } + + public static function getFilterArrayValueDelimiter(): string + { + return static::$filterArrayValueDelimiter; + } } diff --git a/tests/FilterTest.php b/tests/FilterTest.php index 89fe087b..e4d934ad 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -510,4 +510,38 @@ protected function createQueryFromFilterRequest(array $filters): QueryBuilder return QueryBuilder::for(TestModel::class, $request); } + + /** @test */ + public function it_can_override_the_array_value_delimiter_for_single_filters() + { + TestModel::create(['name' => '>XZII/Q1On']); + TestModel::create(['name' => 'h4S4MG3(+>azv4z/I']); + + // First use default delimiter + $models = $this + ->createQueryFromFilterRequest([ + 'ref_id' => 'h4S4MG3(+>azv4z/I,>XZII/Q1On', + ]) + ->allowedFilters(AllowedFilter::exact('ref_id', 'name', true)) + ->get(); + $this->assertEquals(2, $models->count()); + + // Custom delimiter + $models = $this + ->createQueryFromFilterRequest([ + 'ref_id' => 'h4S4MG3(+>azv4z/I|>XZII/Q1On', + ]) + ->allowedFilters(AllowedFilter::exact('ref_id', 'name', true, '|')) + ->get(); + $this->assertEquals(2, $models->count()); + + // Custom delimiter, but default in request + $models = $this + ->createQueryFromFilterRequest([ + 'ref_id' => 'h4S4MG3(+>azv4z/I,>XZII/Q1On', + ]) + ->allowedFilters(AllowedFilter::exact('ref_id', 'name', true, '|')) + ->get(); + $this->assertEquals(0, $models->count()); + } }