Skip to content

Commit 73d64f7

Browse files
committed
Add transformers (with default opinied strategy).
By my opinion I've implemented default strategy based on my usage on all projects: - **string** -> string is always trimmed and if it is empty then it will be transformed to null - **bool** -> Converts most used representations of boolean in string or number ('yes','no',1,0,'1','0','true','false') and converts it to bool. Breaking change: GetValue constructor arguments has changed.
1 parent 6b38e7f commit 73d64f7

37 files changed

+1095
-81
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ help with the documentation, new features, tests).
1717

1818
- 🚀 Retrieve values from Array (JSON) / XML with correct return type
1919
- 🏆 Makes PHPStan / IDE happy due the return types
20-
- 🤹‍ Ensures that desired value is in correct type (without additional loop validation. Validates always on get).
20+
- 🤹‍ Validation: Ensures that desired value is in correct type (without additional loop validation. Validation is always on
21+
while calling get* method).
22+
- 🛠 Transformers: Ensures that values are in expected type (ensures that string is trimmed and empty string converted to
23+
null, accepts bool as string, can be changed.)
2124

2225
```php
2326
$data = new \Wrkflow\GetValue\GetValue(new \Wrkflow\GetValue\DataHolders\ArrayData([

composer.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
}
1111
],
1212
"require-dev": {
13-
"phpstan/phpstan": "^1.6.3",
14-
"phpstan/phpstan-deprecation-rules": "^1.0.0",
15-
"phpstan/phpstan-phpunit": "^1.1.1",
16-
"phpunit/phpunit": "^9.5.20",
17-
"rector/rector": "^0.12.22",
18-
"symplify/easy-coding-standard": "^10.2.2",
13+
"phpstan/phpstan": "1.7.15",
14+
"phpstan/phpstan-deprecation-rules": "1.0.0",
15+
"phpstan/phpstan-phpunit": "1.1.1",
16+
"phpunit/phpunit": "9.5.21",
17+
"rector/rector": "0.13.6",
18+
"symplify/easy-coding-standard": "11.0.5",
1919
"ext-simplexml": "*"
2020
},
2121
"scripts": {

docs/content/en/index.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ position: 1
1111

1212
- 🚀 Retrieve values from Array (JSON) / XML with correct return type
1313
- 🏆 Makes PHPStan / IDE happy due the return types
14-
- 🤹‍ Ensures that desired value is in correct type (without additional loop validation. Validates always on get).
14+
- 🤹‍ Validation: Ensures that desired value is in correct type (without additional loop validation. Validation is always on
15+
while calling get* method).
16+
- 🛠 Transformers: Ensures that values are in expected type (ensures that string is trimmed and empty string converted to
17+
null, accepts bool as string, can be changed.)
1518

1619
## Installation
1720

docs/content/en/transformers.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
---
2+
title: Transformers
3+
subtitle: 'Transform data to expected state'
4+
position: 3
5+
---
6+
7+
## Features
8+
9+
- All transformers can be combined.
10+
- All `get` methods has `transformers` argument
11+
- You are free to add more transformers to this package.
12+
13+
## Strategy
14+
15+
I've chosen most used combination while working with external services and created `DefaultTransformerStrategy`.
16+
17+
You can disable or [change](#customization) this strategy while constructing `GetValue` instance.
18+
19+
```php
20+
$data = new GetValue(data: $array, transformerStrategy: new NoTransformerStrategy());
21+
```
22+
23+
## Transformers
24+
25+
> Transformers parameter in get methods overrides transformers from strategy.
26+
27+
To disable default transformers set `transformers` argument to empty array.
28+
29+
```php
30+
$transformer = new ClosureTransformer(function (mixed $value, string $key): ?string {
31+
if ($value === null) {
32+
return null;
33+
}
34+
35+
return md5($value);
36+
});
37+
$md5 = $getValue->getString('key', [$transformer]);
38+
```
39+
40+
### ClosureTransformer
41+
42+
Transforms the value using closure after validation has been done (can be changed with `$beforeValidation` argument).
43+
44+
45+
```php
46+
$getValue->getBool('key', [new \Wrkflow\GetValue\Transformers\TransformToBool()]);
47+
```
48+
49+
### TransformToBool
50+
51+
Transforms most used representations of boolean in string or number ('yes','no',1,0,'1','0','true','false') and converts
52+
it to bool **before** validation starts.
53+
54+
```php
55+
$getValue->getBool('key', [new \Wrkflow\GetValue\Transformers\TransformToBool()]);
56+
```
57+
58+
### TrimAndEmptyStringToNull
59+
60+
> Used in DefaultTransformerStrategy
61+
62+
Ensures that string is trimmed and transformed to null (if empty string is provided) **before** validation starts.
63+
64+
```php
65+
$getValue->getString('key', [new \Wrkflow\GetValue\Transformers\TrimAndEmptyStringToNull()]);
66+
```
67+
68+
### TrimString
69+
70+
Ensures that string is trimmed **before** validation starts.
71+
72+
```php
73+
// Get trimmed string (no '' to null transformation)
74+
$getValue->getString('key', [new \Wrkflow\GetValue\Transformers\TrimString()]);
75+
```
76+
77+
## Customization
78+
79+
You can create your own transformer by extending:
80+
81+
- For array `Wrkflow\GetValue\Contracts\TransformerArrayContract`
82+
- Reset of values `Wrkflow\GetValue\Contracts\TransformerContract`
83+
84+
Then implement `public function transform(mixed $value, string $key): mixed;`. Expect invalid value and make do not
85+
transform the value if it is invalid. Just return it.
86+
87+
Then implement `public function beforeValidation(mixed $value, string $key): bool;` which ensures that transformation
88+
is not done before validation.
89+
90+
Then change the strategy:
91+
92+
```php
93+
$data = new GetValue(data: $array, transformerStrategy: new MyTransformerStrategy());
94+
```

docs/content/en/validation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ position: 2
77
## Rules
88

99
- All rules can be combined (all must be successful).
10-
- `get` methods has `rules` parameter. Check IDE autocomplete.
10+
- Except array* `get` methods has `rules` argument.
1111
- You can create your own rules by extending `Wrkflow\GetValue\Contracts\RuleContract`
1212
- You are free to add more rules to this package.
1313

src/Actions/GetValidatedValueAction.php

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,48 @@
44

55
namespace Wrkflow\GetValue\Actions;
66

7-
use Wrkflow\GetValue\Contracts\ExceptionBuilderContract;
87
use Wrkflow\GetValue\Contracts\RuleContract;
8+
use Wrkflow\GetValue\Contracts\TransformerContract;
99
use Wrkflow\GetValue\DataHolders\AbstractData;
1010

1111
class GetValidatedValueAction
1212
{
13-
public function __construct(protected readonly ExceptionBuilderContract $exceptionBuilder)
14-
{
13+
public function __construct(
14+
private readonly ValidateAction $validateAction
15+
) {
1516
}
1617

1718
/**
18-
* @param array<RuleContract> $rules
19+
* @param array<RuleContract> $rules
20+
* @param array<TransformerContract> $transforms
1921
*/
20-
public function execute(string $key, AbstractData $data, array $rules): mixed
22+
public function execute(AbstractData $data, string $key, array $rules, array $transforms): mixed
2123
{
2224
$value = $data->getValue($key);
2325

26+
$afterValidationTransforms = [];
27+
28+
foreach ($transforms as $transform) {
29+
if ($rules === [] || $transform->beforeValidation(value: $value, key: $key)) {
30+
$value = $transform->transform(value: $value, key: $key);
31+
} else {
32+
$afterValidationTransforms[] = $transform;
33+
}
34+
}
35+
2436
if ($rules === []) {
2537
return $value;
2638
}
2739

28-
// Skip validation on null value
40+
// Do not run validation on null
2941
if ($value === null) {
30-
return $value;
42+
return null;
3143
}
3244

33-
foreach ($rules as $rule) {
34-
if ($rule->passes($value) === false) {
35-
throw $this->exceptionBuilder->validationFailed($key, $rule::class);
36-
}
45+
$this->validateAction->execute(rules: $rules, value: $value, key: $key);
46+
47+
foreach ($afterValidationTransforms as $transform) {
48+
$value = $transform->transform($value, $key);
3749
}
3850

3951
return $value;

src/Actions/ValidateAction.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValue\Actions;
6+
7+
use Wrkflow\GetValue\Contracts\ExceptionBuilderContract;
8+
9+
class ValidateAction
10+
{
11+
public function __construct(
12+
private readonly ExceptionBuilderContract $exceptionBuilder,
13+
) {
14+
}
15+
16+
public function execute(array $rules, mixed $value, string $key): void
17+
{
18+
foreach ($rules as $rule) {
19+
if ($rule->passes($value) === false) {
20+
throw $this->exceptionBuilder->validationFailed($key, $rule::class);
21+
}
22+
}
23+
}
24+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValue\Contracts;
6+
7+
interface TransformerArrayContract extends TransformerContract
8+
{
9+
public function transform(mixed $value, string $key): array;
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValue\Contracts;
6+
7+
interface TransformerContract
8+
{
9+
public function beforeValidation(mixed $value, string $key): bool;
10+
11+
public function transform(mixed $value, string $key): mixed;
12+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValue\Contracts;
6+
7+
interface TransformerStrategy
8+
{
9+
/**
10+
* @return array<TransformerContract>
11+
*/
12+
public function string(): array;
13+
14+
/**
15+
* @return array<TransformerContract>
16+
*/
17+
public function int(): array;
18+
19+
/**
20+
* @return array<TransformerContract>
21+
*/
22+
public function bool(): array;
23+
24+
/**
25+
* @return array<TransformerContract>
26+
*/
27+
public function dateTime(): array;
28+
29+
/**
30+
* @return array<TransformerContract>
31+
*/
32+
public function float(): array;
33+
34+
/**
35+
* @return array<TransformerArrayContract>
36+
*/
37+
public function array(): array;
38+
}

0 commit comments

Comments
 (0)