Skip to content

Commit c94ee4d

Browse files
committed
Add array closure transformers
1 parent 73d64f7 commit c94ee4d

8 files changed

+361
-55
lines changed

docs/content/en/transformers.md

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,12 @@ $data = new GetValue(data: $array, transformerStrategy: new NoTransformerStrateg
2222

2323
## Transformers
2424

25-
> Transformers parameter in get methods overrides transformers from strategy.
25+
> Transformers argument in get* methods overrides transformers from strategy.
2626
2727
To disable default transformers set `transformers` argument to empty array.
2828

2929
```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()]);
30+
$value = $getValue->getString('key', []);
4731
```
4832

4933
### TransformToBool
@@ -74,6 +58,57 @@ Ensures that string is trimmed **before** validation starts.
7458
$getValue->getString('key', [new \Wrkflow\GetValue\Transformers\TrimString()]);
7559
```
7660

61+
### ClosureTransformer
62+
63+
> Can't be used with get\*Array\* methods.
64+
65+
Transforms the value using closure. Ensure you are returning correct type based on the `get` method you have choosed.
66+
67+
```php
68+
$transformer = new ClosureTransformer(function (mixed $value, string $key): ?string {
69+
if ($value === null) {
70+
return null;
71+
}
72+
73+
return md5($value);
74+
});
75+
$md5 = $getValue->getString('key', [$transformer]);
76+
```
77+
78+
### ClosureArrayTransformer
79+
80+
> Cant be used only with get\*Array\* methods.
81+
82+
Transforms valid array using closure. Always return an array.
83+
84+
```php
85+
$transformer = new ClosureArrayTransformer(function (array $value, string $key): array {
86+
return array_map(fn (string $value) => md5($value), $value);
87+
});
88+
89+
$values = $getValue->getArray('key', [$transformer]);
90+
```
91+
92+
### ClosureArrayItemsTransformer
93+
94+
> Cant be used only with get\*Array\* methods.
95+
96+
Transforms **each value in an array** using closure.
97+
98+
```php
99+
$transformer = new ClosureArrayItemsTransformer( function (mixed $value, string $key): string {
100+
$this->assertEquals('test', $key, 'Key does not match up');
101+
102+
if (is_string($value) !== null) {
103+
throw new ValidationFailedException($key, 'array value not a string');
104+
}
105+
106+
return md5($value);
107+
});
108+
109+
$values = $getValue->getArray('key', [$transformer]);
110+
```
111+
77112
## Customization
78113

79114
You can create your own transformer by extending:

src/Contracts/TransformerArrayContract.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66

77
interface TransformerArrayContract extends TransformerContract
88
{
9-
public function transform(mixed $value, string $key): array;
9+
public function transform(mixed $value, string $key): ?array;
1010
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValue\Transformers;
6+
7+
use Closure;
8+
use Wrkflow\GetValue\Contracts\TransformerArrayContract;
9+
10+
/**
11+
* Re-build the array with a closure for each item
12+
*/
13+
class ClosureArrayItemsTransformer implements TransformerArrayContract
14+
{
15+
/**
16+
* @param Closure(mixed,string):(array|null) $onItem
17+
* @param bool $beforeValidation
18+
*/
19+
public function __construct(
20+
private readonly Closure $onItem,
21+
private readonly bool $beforeValidation = false
22+
) {
23+
}
24+
25+
public function beforeValidation(mixed $value, string $key): bool
26+
{
27+
return $this->beforeValidation;
28+
}
29+
30+
public function transform(mixed $value, string $key): ?array
31+
{
32+
if (is_array($value) === false) {
33+
return null;
34+
}
35+
36+
$items = [];
37+
foreach ($value as $index => $item) {
38+
$items[$index] = call_user_func_array($this->onItem, [$item, $key]);
39+
}
40+
41+
return $items;
42+
}
43+
}
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\Transformers;
6+
7+
use Closure;
8+
use Wrkflow\GetValue\Contracts\TransformerArrayContract;
9+
10+
/**
11+
* Transforms the value using closure after validation has been done.
12+
*/
13+
class ClosureArrayTransformer implements TransformerArrayContract
14+
{
15+
/**
16+
* @param Closure(array,string):array $closure
17+
* @param bool $beforeValidation
18+
*/
19+
public function __construct(
20+
private readonly Closure $closure,
21+
private readonly bool $beforeValidation = false
22+
) {
23+
}
24+
25+
public function beforeValidation(mixed $value, string $key): bool
26+
{
27+
return $this->beforeValidation;
28+
}
29+
30+
public function transform(mixed $value, string $key): ?array
31+
{
32+
if (is_array($value) === false) {
33+
return null;
34+
}
35+
36+
return call_user_func_array($this->closure, [$value, $key]);
37+
}
38+
}

tests/Transformers/AbstractTransformerTestCase.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ public function data(): array
3737
$this->assertNotEmpty($expectation);
3838

3939
$entity = $expectation[0];
40-
$data[$entity->value . ' #' . $index] = $expectation;
40+
41+
if (is_string($entity->value)) {
42+
$data[$entity->value . ' #' . $index] = $expectation;
43+
} else {
44+
$data[] = $expectation;
45+
}
4146
}
4247

4348
return $data;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValueTests\Transformers;
6+
7+
use Closure;
8+
use Wrkflow\GetValue\Contracts\TransformerArrayContract;
9+
use Wrkflow\GetValue\Exceptions\ValidationFailedException;
10+
use Wrkflow\GetValue\Transformers\ClosureArrayItemsTransformer;
11+
12+
class ClosureArrayItemsTransformerTest extends ClosureArrayTransformerTest
13+
{
14+
protected function getClosure(): Closure
15+
{
16+
return function (mixed $value, string $key): string {
17+
$this->assertEquals('test', $key, 'Key does not match up');
18+
19+
if (is_string($value) === false) {
20+
throw new ValidationFailedException($key, 'array value not a string');
21+
}
22+
23+
return md5($value);
24+
};
25+
}
26+
27+
protected function getDefaultTransformer(): TransformerArrayContract
28+
{
29+
return new ClosureArrayItemsTransformer(onItem: $this->getClosure());
30+
}
31+
32+
protected function getBeforeValidationTransformer(): TransformerArrayContract
33+
{
34+
return new ClosureArrayItemsTransformer(onItem: $this->getClosure(), beforeValidation: true);
35+
}
36+
37+
protected function getForceAfterValidation(): TransformerArrayContract
38+
{
39+
return new ClosureArrayItemsTransformer(onItem: $this->getClosure(), beforeValidation: false);
40+
}
41+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValueTests\Transformers;
6+
7+
use Closure;
8+
use Wrkflow\GetValue\Contracts\TransformerArrayContract;
9+
use Wrkflow\GetValue\Transformers\ClosureArrayTransformer;
10+
11+
class ClosureArrayTransformerTest extends AbstractTransformerTestCase
12+
{
13+
public function dataToTest(): array
14+
{
15+
$transformer = $this->getDefaultTransformer();
16+
17+
return $this->dataAfterValidationForTransformer($transformer);
18+
}
19+
20+
public function dataToTestBeforeValidation(): array
21+
{
22+
$transformer = $this->getBeforeValidationTransformer();
23+
24+
return [
25+
[
26+
new TransformerExpectationEntity(
27+
value: [''],
28+
transformer: $transformer,
29+
expectedValue: ['d41d8cd98f00b204e9800998ecf8427e']
30+
),
31+
],
32+
[
33+
new TransformerExpectationEntity(
34+
value: [' '],
35+
transformer: $transformer,
36+
expectedValue: ['7215ee9c7d9dc229d2921a40e899ec5f']
37+
),
38+
],
39+
[
40+
new TransformerExpectationEntity(
41+
value: [' asd '],
42+
transformer: $transformer,
43+
expectedValue: ['81c24eeebdef51c832407fa3e4509ab8']
44+
),
45+
],
46+
[
47+
new TransformerExpectationEntity(
48+
value: ['asd '],
49+
transformer: $transformer,
50+
expectedValue: ['4fe2077508f28d88bfa1473149415224']
51+
),
52+
],
53+
[
54+
new TransformerExpectationEntity(
55+
value: ['asd mix'],
56+
transformer: $transformer,
57+
expectedValue: ['bf40744fb5eeca1029aed8d8c5d30f82']
58+
),
59+
],
60+
[new TransformerExpectationEntity(value: null, transformer: $transformer, expectedValue: null)],
61+
];
62+
}
63+
64+
/**
65+
* @dataProvider dataToTestBeforeValidation
66+
*/
67+
public function testBeforeValidation(TransformerExpectationEntity $entity): void
68+
{
69+
$this->assertValue($entity);
70+
}
71+
72+
public function dataToAfterValidationForce(): array
73+
{
74+
$transformer = $this->getForceAfterValidation();
75+
76+
return $this->dataAfterValidationForTransformer($transformer);
77+
}
78+
79+
/**
80+
* @dataProvider dataToAfterValidationForce
81+
*/
82+
public function testAfterValidationForce(TransformerExpectationEntity $entity): void
83+
{
84+
$this->assertValue($entity);
85+
}
86+
87+
protected function dataAfterValidationForTransformer(TransformerArrayContract $transformer): array
88+
{
89+
return [
90+
[
91+
new TransformerExpectationEntity(
92+
value: [''],
93+
transformer: $transformer,
94+
expectedValue: ['d41d8cd98f00b204e9800998ecf8427e'],
95+
expectedValueBeforeValidation: ['']
96+
),
97+
],
98+
[
99+
new TransformerExpectationEntity(
100+
value: [' '],
101+
transformer: $transformer,
102+
expectedValue: ['7215ee9c7d9dc229d2921a40e899ec5f'],
103+
expectedValueBeforeValidation: [' ']
104+
),
105+
],
106+
[
107+
new TransformerExpectationEntity(
108+
value: [' asd '],
109+
transformer: $transformer,
110+
expectedValue: ['81c24eeebdef51c832407fa3e4509ab8'],
111+
expectedValueBeforeValidation: [' asd ']
112+
),
113+
],
114+
[
115+
new TransformerExpectationEntity(
116+
value: ['asd '],
117+
transformer: $transformer,
118+
expectedValue: ['4fe2077508f28d88bfa1473149415224'],
119+
expectedValueBeforeValidation: ['asd ']
120+
),
121+
],
122+
[
123+
new TransformerExpectationEntity(
124+
value: ['asd mix'],
125+
transformer: $transformer,
126+
expectedValue: ['bf40744fb5eeca1029aed8d8c5d30f82'],
127+
expectedValueBeforeValidation: ['asd mix']
128+
),
129+
],
130+
// Closure not called
131+
[new TransformerExpectationEntity(value: null, transformer: $transformer, expectedValue: null)],
132+
];
133+
}
134+
135+
protected function getClosure(): Closure
136+
{
137+
return function (array $value, string $key): array {
138+
$this->assertEquals('test', $key, 'Key does not match up');
139+
140+
return array_map(fn (string $item) => md5($item), $value);
141+
};
142+
}
143+
144+
protected function getDefaultTransformer(): TransformerArrayContract
145+
{
146+
return new ClosureArrayTransformer($this->getClosure());
147+
}
148+
149+
protected function getBeforeValidationTransformer(): TransformerArrayContract
150+
{
151+
return new ClosureArrayTransformer(closure: $this->getClosure(), beforeValidation: true);
152+
}
153+
154+
protected function getForceAfterValidation(): TransformerArrayContract
155+
{
156+
return new ClosureArrayTransformer(closure: $this->getClosure(), beforeValidation: false);
157+
}
158+
}

0 commit comments

Comments
 (0)