Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .coveralls.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
php-versions: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
31 changes: 0 additions & 31 deletions .scrutinizer.yml

This file was deleted.

46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,52 @@ $specification = [
];
```

## requires

#### Summary

Defines any input field(s) which are required by a separate given field. Used when one field requires one or more additional fields to be operate properly.

#### Types

* string||string[]

#### Default

There is no default value for this option.

#### Constant

```php
TraderInteractive\FilterOptions::REQUIRES
```

#### Examples

```php
$specification = [
'duration' => [['uint']],
'startDate' => [
TraderInteractive\FilterOptions::REQUIRES => 'duration',
['date'],
],
];
```

```php
$specification = [
'id' => [['string']],
'typeId' => [
TraderInteractive\FilterOptions::REQUIRES => ['typeCode'],
['uint'],
],
'typeCode' => [
TraderInteractive\FilterOptions::REQUIRES => ['typeId'],
['string'],
],
];
```

## uses

#### Summary
Expand Down
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@
},
"require-dev": {
"phpunit/phpunit": "^9.0",
"squizlabs/php_codesniffer": "^3.2",
"symfony/yaml": "^3.4"
"squizlabs/php_codesniffer": "^3.2"
},
"autoload": {
"psr-4": { "TraderInteractive\\": "src/" }
Expand Down
5 changes: 5 additions & 0 deletions src/FilterOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ final class FilterOptions
*/
const IS_REQUIRED = 'required';

/**
* @var string
*/
const REQUIRES = 'requires';

/**
* @var string
*/
Expand Down
38 changes: 38 additions & 0 deletions src/Filterer.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ public function execute(array $input) : FilterResponse
$filteredInput = [];
$errors = [];
$conflicts = [];
$requires = [];
foreach ($inputToFilter as $field => $input) {
$filters = $this->specification[$field];
self::assertFiltersIsAnArray($filters, $field);
Expand All @@ -170,6 +171,7 @@ public function execute(array $input) : FilterResponse
unset($filters[FilterOptions::IS_REQUIRED]);//doesn't matter if required since we have this one
unset($filters[FilterOptions::DEFAULT_VALUE]);//doesn't matter if there is a default since we have a value
$conflicts = self::extractConflicts($filters, $field, $conflicts);
$requires = self::extractRequires($filters, $field, $requires);

foreach ($filters as $filter) {
self::assertFilterIsArray($filter, $field);
Expand Down Expand Up @@ -218,6 +220,7 @@ public function execute(array $input) : FilterResponse

$errors = self::handleAllowUnknowns($this->allowUnknowns, $leftOverInput, $errors);
$errors = self::handleConflicts($filteredInput, $conflicts, $errors);
$errors = self::handleRequires($filteredInput, $requires, $errors);

return new FilterResponse($filteredInput, $errors, $leftOverInput);
}
Expand Down Expand Up @@ -249,6 +252,41 @@ private static function extractConflicts(array &$filters, string $field, array $
return $conflicts;
}

private static function extractRequires(array &$filters, string $field, array $requires): array
{
$required = $filters[FilterOptions::REQUIRES] ?? null;
unset($filters[FilterOptions::REQUIRES]);
if ($required === null) {
return $requires;
}

if (!is_array($required)) {
$required = [$required];
}

$requires[$field] = $required;

return $requires;
}

private static function handleRequires(array $inputToFilter, array $requires, array $errors)
{
foreach (array_keys($inputToFilter) as $field) {
if (!array_key_exists($field, $requires)) {
continue;
}


foreach ($requires[$field] as $requiredField) {
if (!array_key_exists($requiredField, $inputToFilter)) {
Comment thread
chadicus marked this conversation as resolved.
$errors[] = "Field '{$field}' requires field '{$requiredField}' but it is not present.";
}
}
}

return $errors;
}

private static function handleConflicts(array $inputToFilter, array $conflicts, array $errors)
{
foreach (array_keys($inputToFilter) as $field) {
Expand Down
64 changes: 64 additions & 0 deletions tests/FiltererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,70 @@ public function provideValidFilterData() : array
'options' => [],
'result' => [true, ['field' => 'a string with newlines and extra spaces'], null, []],
],
'requires single' => [
'spec' => [
'fieldOne' => [FilterOptions::REQUIRES => 'fieldThree', ['string']],
'fieldTwo' => [['string']],
'fieldThree' => [['string']],
],
'input' => [
'fieldOne' => 'abc',
'fieldTwo' => '123',
],
'options' => [],
'result' => [
false,
null,
"Field 'fieldOne' requires field 'fieldThree' but it is not present.",
[],
],
],
'requires multiple' => [
'spec' => [
'fieldOne' => [FilterOptions::REQUIRES => ['fieldThree', 'fieldFour'], ['string']],
'fieldTwo' => [['string']],
'fieldThree' => [['string']],
'fieldFour' => [['uint']],
],
'input' => [
'fieldOne' => 'abc',
'fieldTwo' => '123',
],
'options' => [],
'result' => [
false,
null,
"Field 'fieldOne' requires field 'fieldThree' but it is not present.\n"
. "Field 'fieldOne' requires field 'fieldFour' but it is not present.",
[],
],
],
'requires multiple all present' => [
'spec' => [
'fieldOne' => [FilterOptions::REQUIRES => ['fieldThree', 'fieldFour'], ['string']],
'fieldTwo' => [['string']],
'fieldThree' => [['string']],
'fieldFour' => [['uint']],
],
'input' => [
'fieldOne' => 'abc',
'fieldTwo' => '123',
'fieldThree' => 'foo',
'fieldFour' => '789',
],
'options' => [],
'result' => [
true,
[
'fieldOne' => 'abc',
'fieldTwo' => '123',
'fieldThree' => 'foo',
'fieldFour' => 789,
],
null,
[],
],
],
'conflicts with single' => [
'spec' => [
'fieldOne' => [FilterOptions::CONFLICTS_WITH => 'fieldThree', ['string']],
Expand Down
Loading