Skip to content

Commit 859e1ca

Browse files
committed
Add responseType option
1 parent 50d86fe commit 859e1ca

4 files changed

Lines changed: 224 additions & 7 deletions

File tree

src/FilterResponse.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace TraderInteractive;
4+
5+
/**
6+
* This object contains the various data returned by a filter action.
7+
*
8+
* @property-read bool $success TRUE if the filter was successful or FALSE if errors were encountered.
9+
* @property-read mixed $filteredValue The input values after being filtered.
10+
* @property-read array $errors Any errors encountered during the filter process.
11+
* @property-read string|null $errorMessage An error message generated from the errors. NULL if no errors.
12+
* @property-read mixed $unknowns The values that were unknown during filtering.
13+
*/
14+
final class FilterResponse
15+
{
16+
/**
17+
* @param array $filteredValue The input values after being filtered.
18+
* @param array $errors Any errors encountered during the filter process.
19+
* @param array $unknowns The values that were unknown during filtering.
20+
*/
21+
public function __construct(
22+
array $filteredValue,
23+
array $errors = [],
24+
array $unknowns = []
25+
) {
26+
$this->success = count($errors) === 0;
27+
$this->filteredValue = $filteredValue;
28+
$this->errors = $errors;
29+
$this->errorMessage = $this->success ? null : implode("\n", $errors);
30+
$this->unknowns = $unknowns;
31+
}
32+
33+
/**
34+
* Converts the response to an array.
35+
*
36+
* @return array
37+
*/
38+
public function toArray() : array
39+
{
40+
$filteredValue = $this->success ? $this->filteredValue : null;
41+
42+
return [
43+
$this->success,
44+
$filteredValue,
45+
$this->errorMessage,
46+
$this->unknowns
47+
];
48+
}
49+
}

src/Filterer.php

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ final class Filterer
4141
'url' => '\\TraderInteractive\\Filter\\Url::filter',
4242
];
4343

44+
/**
45+
* @var string
46+
*/
47+
const RESPONSE_TYPE_ARRAY = 'array';
48+
49+
/**
50+
* @var string
51+
*/
52+
const RESPONSE_TYPE_FILTER = FilterResponse::class;
53+
4454
/**
4555
* @var array
4656
*/
@@ -106,18 +116,23 @@ final class Filterer
106116
* @param array $options 'allowUnknowns' (default false) true to allow unknowns or false to treat as error,
107117
* 'defaultRequired' (default false) true to make fields required by default and treat as
108118
* error on absence and false to allow their absence by default
119+
* 'responseType' (default RESPONSE_TYPE_ARRAY) Determines the return type, as described
120+
* in the return section.
109121
*
110-
* @return array on success [true, $input filtered, null, array of unknown fields]
111-
* on error [false, null, 'error message', array of unknown fields]
122+
* @return array|FilterResponse If 'responseType' option is RESPONSE_TYPE_ARRAY:
123+
* on success [true, $input filtered, null, array of unknown fields]
124+
* on error [false, null, 'error message', array of unknown fields]
125+
* If 'responseType' option is RESPONSE_TYPE_FILTER: a FilterResponse instance.
112126
*
113127
* @throws Exception
114128
* @throws InvalidArgumentException if 'allowUnknowns' option was not a bool
115129
* @throws InvalidArgumentException if 'defaultRequired' option was not a bool
130+
* @throws InvalidArgumentException if 'responseType' option was not a recognized type
116131
* @throws InvalidArgumentException if filters for a field was not an array
117132
* @throws InvalidArgumentException if a filter for a field was not an array
118133
* @throws InvalidArgumentException if 'required' for a field was not a bool
119134
*/
120-
public static function filter(array $spec, array $input, array $options = []) : array
135+
public static function filter(array $spec, array $input, array $options = [])
121136
{
122137
$options += ['allowUnknowns' => false, 'defaultRequired' => false];
123138

@@ -172,11 +187,9 @@ public static function filter(array $spec, array $input, array $options = []) :
172187

173188
$errors = self::handleAllowUnknowns($allowUnknowns, $leftOverInput, $errors);
174189

175-
if (empty($errors)) {
176-
return [true, $inputToFilter, null, $leftOverInput];
177-
}
190+
$responseType = $options['responseType'] ?? self::RESPONSE_TYPE_ARRAY;
178191

179-
return [false, null, implode("\n", $errors), $leftOverInput];
192+
return self::generateFilterResponse($responseType, $inputToFilter, $errors, $leftOverInput);
180193
}
181194

182195
/**
@@ -453,4 +466,33 @@ private static function getDefaultRequired(array $options) : bool
453466

454467
return $defaultRequired;
455468
}
469+
470+
/**
471+
* @param string $responseType The type of object that should be returned.
472+
* @param array $filteredValue The filtered input to return.
473+
* @param array $errors The errors to return.
474+
* @param array $unknowns The unknowns to return.
475+
*
476+
* @return array|FilterResponse
477+
*
478+
* @see filter For more information on how responseType is handled and returns are structured.
479+
*/
480+
private static function generateFilterResponse(
481+
string $responseType,
482+
array $filteredValue,
483+
array $errors,
484+
array $unknowns
485+
) {
486+
$filterResponse = new FilterResponse($filteredValue, $errors, $unknowns);
487+
488+
if ($responseType === self::RESPONSE_TYPE_FILTER) {
489+
return $filterResponse;
490+
}
491+
492+
if ($responseType === self::RESPONSE_TYPE_ARRAY) {
493+
return $filterResponse->toArray();
494+
}
495+
496+
throw new InvalidArgumentException("'responseType' was not a recognized value");
497+
}
456498
}

tests/FilterResponseTest.php

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
namespace TraderInteractiveTest;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use TraderInteractive\FilterResponse;
7+
8+
/**
9+
* @coversDefaultClass \TraderInteractive\FilterResponse
10+
*/
11+
class FilterResponseTest extends TestCase
12+
{
13+
/**
14+
* @test
15+
* @covers ::__construct
16+
*/
17+
public function construct()
18+
{
19+
$value = ['foo' => 'bar'];
20+
$errors = [];
21+
$unknowns = ['other' => 'unknown'];
22+
23+
$response = new FilterResponse($value, $errors, $unknowns);
24+
25+
$this->assertSame(true, $response->success);
26+
$this->assertSame($value, $response->filteredValue);
27+
$this->assertSame($errors, $response->errors);
28+
$this->assertSame(null, $response->errorMessage);
29+
$this->assertSame($unknowns, $response->unknowns);
30+
}
31+
32+
/**
33+
* @test
34+
* @covers ::__construct
35+
*/
36+
public function constructWithErrors()
37+
{
38+
$value = ['foo' => 'bar'];
39+
$errors = ['something bad happened', 'and something else too'];
40+
$unknowns = ['other' => 'unknown'];
41+
42+
$response = new FilterResponse($value, $errors, $unknowns);
43+
44+
$this->assertSame(false, $response->success);
45+
$this->assertSame($value, $response->filteredValue);
46+
$this->assertSame($errors, $response->errors);
47+
$this->assertSame("something bad happened\nand something else too", $response->errorMessage);
48+
$this->assertSame($unknowns, $response->unknowns);
49+
}
50+
51+
/**
52+
* @test
53+
* @covers ::__construct
54+
*/
55+
public function constructDefault()
56+
{
57+
$input = ['filtered' => 'input'];
58+
59+
$response = new FilterResponse($input);
60+
61+
$this->assertSame(true, $response->success);
62+
$this->assertSame($input, $response->filteredValue);
63+
$this->assertSame([], $response->errors);
64+
$this->assertSame(null, $response->errorMessage);
65+
$this->assertSame([], $response->unknowns);
66+
}
67+
68+
/**
69+
* @test
70+
* @covers ::toArray
71+
* @dataProvider provideToArray
72+
*
73+
* @param array $value The filtered value to pass to the response.
74+
* @param array $errors The errors to pass to the response.
75+
* @param array $unknowns The unknowns to pass to the response.
76+
* @param array $expected The expected array value.
77+
*/
78+
public function toArray(array $value, array $errors, array $unknowns, array $expected)
79+
{
80+
$response = new FilterResponse($value, $errors, $unknowns);
81+
$arrayResponse = $response->toArray();
82+
83+
$this->assertSame($expected, $arrayResponse);
84+
}
85+
86+
/**
87+
* @return array
88+
*/
89+
public function provideToArray() : array
90+
{
91+
return [
92+
'success' => [
93+
'input' => ['foo' => 'bar'],
94+
'errors' => [],
95+
'unknowns' => ['other' => 'unknown'],
96+
'expected' => [true, ['foo' => 'bar'], null, ['other' => 'unknown']],
97+
],
98+
'failure' => [
99+
'input' => ['foo' => 'bar'],
100+
'errors' => ['something bad happened', 'and something else too'],
101+
'unknowns' => ['other' => 'unknown'],
102+
'expected' => [false, null, "something bad happened\nand something else too", ['other' => 'unknown']],
103+
],
104+
];
105+
}
106+
}

tests/FiltererTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,26 @@ public function provideValidFilterData() : array
225225
];
226226
}
227227

228+
/**
229+
* @test
230+
* @covers ::filter
231+
*/
232+
public function filterReturnsResponseType()
233+
{
234+
$specification = ['id' => [['uint']]];
235+
$input = ['id' => 1];
236+
$options = ['responseType' => Filterer::RESPONSE_TYPE_FILTER];
237+
238+
$result = Filterer::filter($specification, $input, $options);
239+
240+
$this->assertInstanceOf(FilterResponse::class, $result);
241+
$this->assertSame(true, $result->success);
242+
$this->assertSame($input, $result->filteredValue);
243+
$this->assertSame([], $result->errors);
244+
$this->assertSame(null, $result->errorMessage);
245+
$this->assertSame([], $result->unknowns);
246+
}
247+
228248
/**
229249
* @test
230250
* @covers ::filter

0 commit comments

Comments
 (0)