Skip to content

Commit 3590bf1

Browse files
authored
Merge pull request #93 from chadicus/fea/return-on-null
Add FilterOptions::RETURN_ON_NULL
2 parents 047bc08 + a582745 commit 3590bf1

File tree

4 files changed

+138
-9
lines changed

4 files changed

+138
-9
lines changed

README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,82 @@ $specification = [
369369

370370
The exponent filter spec will call the PHP function `pow()` with the value provided and the result of the filtered `base`
371371

372+
## throwOnError
373+
374+
#### Summary
375+
376+
If true the Filterer will throw any exception caught when filtering a value instead of returning the error in the filter response.
377+
378+
#### Types
379+
380+
* boolean
381+
382+
#### Default
383+
384+
The default value for this option is `false`
385+
386+
#### Constant
387+
388+
```php
389+
TraderInteractive\FilterOptions::THROW_ON_ERROR
390+
```
391+
392+
#### Example
393+
394+
```php
395+
$idFilter = function ($id) : int {
396+
if (!is_int($id)) {
397+
throw new NotFoundException("id '{$id}' was not found");
398+
}
399+
400+
return $id;
401+
};
402+
$specification = [
403+
'id' => [
404+
\TraderInteractive\FilterOptions::THROW_ON_ERROR => true,
405+
[$idFilter],
406+
],
407+
];
408+
```
409+
410+
If the `id` value given in the input is not an integer the Filterer::execute() will throw the `NotFoundException`
411+
412+
## returnOnNull
413+
414+
#### Summary
415+
416+
Flag to break the filter chain if a resulting value is `null` Useful for nullable fields which require additional filtering if the value is not null.
417+
418+
#### Types
419+
420+
* boolean
421+
422+
#### Default
423+
424+
The default value for this option is `false`
425+
426+
#### Constant
427+
428+
```php
429+
TraderInteractive\FilterOptions::RETURN_ON_NULL
430+
```
431+
432+
#### Example
433+
434+
```php
435+
$validCodes = ['A', 'I', 'X'];
436+
$specification = [
437+
'code' => [
438+
\TraderInteractive\FilterOptions::RETURN_ON_NULL => true,
439+
['string', true],
440+
['strtoupper'],
441+
['in', $validCodes],
442+
],
443+
];
444+
```
445+
446+
If the `code` value is `null` then the resulting filtered value will be null. Otherwise the value must be one of the `$validCode` values.
447+
372448
### Included Filters
373449
Of course, any function can potentially be used as a filter, but we include some useful filters with aliases for common circumstances.
374450

src/FilterOptions.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ final class FilterOptions
77
/**
88
* @var string
99
*/
10-
const DEFAULT_VALUE = 'default';
10+
const CONFLICTS_WITH = 'conflictsWith';
1111

1212
/**
1313
* @var string
@@ -17,20 +17,25 @@ final class FilterOptions
1717
/**
1818
* @var string
1919
*/
20-
const IS_REQUIRED = 'required';
20+
const DEFAULT_VALUE = 'default';
2121

2222
/**
2323
* @var string
2424
*/
25-
const CONFLICTS_WITH = 'conflictsWith';
25+
const IS_REQUIRED = 'required';
2626

2727
/**
2828
* @var string
2929
*/
30-
const USES = 'uses';
30+
const RETURN_ON_NULL = 'returnOnNull';
3131

3232
/**
3333
* @var string
3434
*/
3535
const THROW_ON_ERROR = 'throwOnError';
36+
37+
/**
38+
* @var string
39+
*/
40+
const USES = 'uses';
3641
}

src/Filterer.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,7 @@ final class Filterer implements FiltererInterface
6464
/**
6565
* @var string
6666
*/
67-
const INVALID_THROW_ON_ERROR_VALUE_ERROR_FORMAT = (
68-
FilterOptions::THROW_ON_ERROR . " for field '%s' was not a boolean value"
69-
);
67+
const INVALID_BOOLEAN_FILTER_OPTION = "%s for field '%s' was not a boolean value";
7068

7169
/**
7270
* @var array
@@ -147,6 +145,7 @@ public function execute(array $input) : FilterResponse
147145
self::assertFiltersIsAnArray($filters, $field);
148146
$customError = self::validateCustomError($filters, $field);
149147
$throwOnError = self::validateThrowOnError($filters, $field);
148+
$returnOnNull = self::validateReturnOnNull($filters, $field);
150149
unset($filters[FilterOptions::IS_REQUIRED]);//doesn't matter if required since we have this one
151150
unset($filters[FilterOptions::DEFAULT_VALUE]);//doesn't matter if there is a default since we have a value
152151
$conflicts = self::extractConflicts($filters, $field, $conflicts);
@@ -169,6 +168,9 @@ public function execute(array $input) : FilterResponse
169168
try {
170169
$this->addUsedInputToFilter($uses, $filteredInput, $field, $filter);
171170
$input = call_user_func_array($function, $filter);
171+
if ($input === null && $returnOnNull) {
172+
break;
173+
}
172174
} catch (Exception $exception) {
173175
if ($throwOnError) {
174176
throw $exception;
@@ -623,7 +625,7 @@ private static function validateThrowOnError(array &$filters, string $field) : b
623625
$throwOnError = $filters[FilterOptions::THROW_ON_ERROR];
624626
if ($throwOnError !== true && $throwOnError !== false) {
625627
throw new InvalidArgumentException(
626-
sprintf(self::INVALID_THROW_ON_ERROR_VALUE_ERROR_FORMAT, $field)
628+
sprintf(self::INVALID_BOOLEAN_FILTER_OPTION, FilterOptions::THROW_ON_ERROR, $field)
627629
);
628630
}
629631

@@ -632,6 +634,24 @@ private static function validateThrowOnError(array &$filters, string $field) : b
632634
return $throwOnError;
633635
}
634636

637+
private static function validateReturnOnNull(array &$filters, string $field) : bool
638+
{
639+
if (!array_key_exists(FilterOptions::RETURN_ON_NULL, $filters)) {
640+
return false;
641+
}
642+
643+
$returnOnNull = $filters[FilterOptions::RETURN_ON_NULL];
644+
if ($returnOnNull !== true && $returnOnNull !== false) {
645+
throw new InvalidArgumentException(
646+
sprintf(self::INVALID_BOOLEAN_FILTER_OPTION, FilterOptions::RETURN_ON_NULL, $field)
647+
);
648+
}
649+
650+
unset($filters[FilterOptions::RETURN_ON_NULL]);
651+
652+
return $returnOnNull;
653+
}
654+
635655
private static function validateCustomError(array &$filters, string $field)
636656
{
637657
$customError = null;

tests/FiltererTest.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,12 @@ function (int $input, int $fieldOneValue) : int {
378378
[],
379379
],
380380
],
381+
'returnOnNull filter option' => [
382+
'spec' => ['field' => [FilterOptions::RETURN_ON_NULL => true, ['string', true], ['string']]],
383+
'input' => ['field' => null],
384+
'options' => [],
385+
'result' => [true, ['field' => null], null, []],
386+
],
381387
];
382388
}
383389

@@ -411,7 +417,9 @@ public function executeThrowsOnError()
411417
public function executeValidatesThrowsOnError()
412418
{
413419
$this->expectException(InvalidArgumentException::class);
414-
$this->expectExceptionMessage(sprintf(Filterer::INVALID_THROW_ON_ERROR_VALUE_ERROR_FORMAT, 'id'));
420+
$this->expectExceptionMessage(
421+
sprintf(Filterer::INVALID_BOOLEAN_FILTER_OPTION, FilterOptions::THROW_ON_ERROR, 'id')
422+
);
415423
$specification = [
416424
'id' => [
417425
FilterOptions::THROW_ON_ERROR => 'abc',
@@ -422,6 +430,26 @@ public function executeValidatesThrowsOnError()
422430
$filterer->execute(['id' => 1]);
423431
}
424432

433+
/**
434+
* @test
435+
* @covers ::execute
436+
*/
437+
public function executeValidatesReturnOnNull()
438+
{
439+
$this->expectException(InvalidArgumentException::class);
440+
$this->expectExceptionMessage(
441+
sprintf(Filterer::INVALID_BOOLEAN_FILTER_OPTION, FilterOptions::RETURN_ON_NULL, 'id')
442+
);
443+
$specification = [
444+
'id' => [
445+
FilterOptions::RETURN_ON_NULL => 'abc',
446+
['uint'],
447+
],
448+
];
449+
$filterer = new Filterer($specification);
450+
$filterer->execute(['id' => 1]);
451+
}
452+
425453
/**
426454
* @test
427455
* @covers ::filter

0 commit comments

Comments
 (0)