Skip to content

Commit b756f82

Browse files
committed
Allow Array*Transformers in all get* methods and ignore null values
- Ignore null values while re-building (ArrayItemTransformer, ArrayItemGetterTransformer) an array - Breaking change: TransformerArrayContract removed
1 parent ab3b8bd commit b756f82

13 files changed

+557
-150
lines changed

docs/content/en/transformers.md

Lines changed: 73 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,30 @@ $transformer = new ArrayTransformer(function (array $value, string $key): array
138138
});
139139

140140
$values = $data->getArray('key', transformers: [$transformer]);
141+
// Result: ['2231d0878e1f14976c498ad49de37ef6', 'edc23e3209134c89922592669e09cb65']
142+
```
143+
144+
If you want to use this transformer with getString and other non-array method then you need to run the
145+
transformer before validation by setting `beforeValidation: true`.
146+
147+
148+
```php
149+
use Wrkflow\GetValue\GetValue;
150+
use Wrkflow\GetValue\DataHolders\ArrayData;
151+
use Wrkflow\GetValue\Transformers\ArrayTransformer;
152+
153+
$data = new GetValue(new ArrayData([
154+
'key' => ['Marco', 'Polo']
155+
]));
156+
157+
$transformer = new ArrayTransformer(
158+
closure: fn (array $value, string $key): string => implode(' ', $value),
159+
beforeValidation: true
160+
);
161+
162+
$name = $data->getString('key', transformers: [$transformer]);
163+
$this->assertEquals('Marco Polo', $name);
164+
// Result: Marco Polo
141165
```
142166

143167
### ArrayItemTransformer
@@ -153,80 +177,88 @@ use Wrkflow\GetValue\Transformers\ArrayItemTransformer;
153177
use Wrkflow\GetValue\Exceptions\ValidationFailedException;
154178

155179
$data = new GetValue(new ArrayData([
156-
'key' => ['Marco', 'Polo']
180+
'names' => [['Marco', 'Polo'], ['Way', 'Point'], []]
157181
]));
158182
$transformer = new ArrayItemTransformer( function (mixed $value, string $key): string {
159-
if (is_string($value) !== null) {
160-
throw new ValidationFailedException($key, 'array value not a string');
161-
}
162-
163-
return md5($value);
183+
if (is_array($value) !== true) {
184+
throw new ValidationFailedException($key, 'expecting an array');
185+
}
186+
187+
if ($value === []) {
188+
return null;
189+
}
190+
191+
return implode(' ', $value);
164192
});
165193

166-
$values = $data->getArray('key', transformers: [$transformer]);
194+
$values = $data->getArray('names', transformers: [$transformer]);
195+
// Result: ['Marco Polo', 'Way Point']
167196
```
168197

169-
### ArrayItemGetterTransformer
170-
171-
> Can be used only with get\*Array\* methods. Throws NotAnArrayException if array value is not an array.
172-
173-
Transforms an **array that contains array values** in a closure that receives wrapped array in GetValue.
198+
If you return `null` in your closure then value is not added to result array. Use `ignoreNullResult: false` in constructor.
174199

175200
```php
176201
use Wrkflow\GetValue\GetValue;
177202
use Wrkflow\GetValue\DataHolders\ArrayData;
178-
use Wrkflow\GetValue\Transformers\ArrayItemGetterTransformer;
203+
use Wrkflow\GetValue\Transformers\ArrayItemTransformer;
204+
use Wrkflow\GetValue\Exceptions\ValidationFailedException;
179205

180206
$data = new GetValue(new ArrayData([
181-
'key' => [['test' => 'Marco'], ['test' => 'Polo']]
207+
'names' => ['Marco Polo', 'Way Point', ''],
182208
]));
183-
$transformer = new ArrayItemGetterTransformer( function (GetValue $value, string $key): string {
184-
return [
185-
'test' => $value->getRequiredString('test'),
186-
];
187-
});
209+
$transformer = new ArrayItemTransformer(function (mixed $value, string $key): ?array {
210+
if (is_string($value) === false) {
211+
throw new ValidationFailedException($key, 'expecting string');
212+
}
188213

189-
$values = $data->getArray('key', transformers: [$transformer]);
214+
if ($value === '') {
215+
return null;
216+
}
217+
218+
return explode(' ', $value);
219+
}, ignoreNullResult: false);
220+
221+
$values = $data->getArray('names', transformers: [$transformer]);
222+
// Result: [['Marco', 'Polo'], ['Way', 'Point'], null]
190223
```
191224

192225
### ArrayItemGetterTransformer
193226

194-
> Can be used only with get\*Array\* methods.
227+
> Can be used only with get\*Array\* methods. Throws NotAnArrayException if array value is not an array.
195228
196-
Transforms an **array** in a closure that receives wrapped array in GetValue.
229+
Transforms an **array that contains array values** in a closure that receives wrapped array in GetValue.
197230

198231
```php
199232
use Wrkflow\GetValue\GetValue;
200233
use Wrkflow\GetValue\DataHolders\ArrayData;
201-
use Wrkflow\GetValue\Transformers\ArrayGetterTransformer;
234+
use Wrkflow\GetValue\Transformers\ArrayItemGetterTransformer;
202235

203236
$data = new GetValue(new ArrayData([
204-
'key' => ['test' => 'Value!']
237+
'names' => [['name' => 'Marco', 'surname' => 'Polo'], ['name' => 'Martin', 'surname' => 'Way']]
205238
]));
206-
$transformer = new ArrayGetterTransformer(function (GetValue $value, string $key): string {
207-
return [
208-
'test' => $value->getRequiredString('test'),
209-
];
239+
240+
$transformer = new ArrayItemGetterTransformer(function (GetValue $value, string $key): string {
241+
return $value->getRequiredString('name') . ' '.$value->getRequiredString('surname');
210242
});
211243

212-
$values = $data->getArray('key', transformers: [$transformer]);
244+
$values = $data->getArray('names', transformers: [$transformer]);
245+
// Result: ['Marco Polo', 'Martin Way']
213246
```
214247

215-
## Customization
216-
217-
You can create your own transformer by extending:
248+
If you return `null` in your closure then value is not added to result array. Use `ignoreNullResult: false` in same way
249+
as in [ArrayItemTransformer](#arrayitemtransformer)`.
218250

219-
- For array `Wrkflow\GetValue\Contracts\TransformerArrayContract`
220-
- Rest of the value types use `Wrkflow\GetValue\Contracts\TransformerContract`
221-
- `$key` contains full path key from the root data. Array notation is converted to dot notation.
222-
223-
Then implement `public function transform(mixed $value, string $key): mixed;`. Expect that you can receive an invalid
224-
value. Return original `$value` if transformation can't be done.
251+
## Customization
225252

226-
Then implement `public function beforeValidation(mixed $value, string $key): bool;` which ensures that transformation
227-
is not done before validation.
253+
You can create your own transformer by extending `Wrkflow\GetValue\Contracts\TransformerContract` class:
228254

229-
Then change the strategy:
255+
1. Then implement `public function transform(mixed $value, string $key): mixed;`. Expect that you can receive an invalid
256+
value. Return original `$value` if transformation can't be done.
257+
2. Then implement `public function beforeValidation(mixed $value, string $key): bool;` which tells if we can transform
258+
the value after or before validation.
259+
- Use before validation for transforming value to a type that `get` method expects.
260+
3. You can use `$key` which contains full path key from the root data. Array notation is converted to dot notation.
261+
4. Then change the strategy:
230262

231263
```php
232264
use Wrkflow\GetValue\GetValue;

src/Contracts/TransformerArrayContract.php

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/Contracts/TransformerStrategy.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public function dateTime(): array;
3232
public function float(): array;
3333

3434
/**
35-
* @return array<TransformerArrayContract>
35+
* @return array<TransformerContract>
3636
*/
3737
public function array(): array;
3838
}

src/GetValue.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use Wrkflow\GetValue\Builders\ExceptionBuilder;
1616
use Wrkflow\GetValue\Contracts\ExceptionBuilderContract;
1717
use Wrkflow\GetValue\Contracts\RuleContract;
18-
use Wrkflow\GetValue\Contracts\TransformerArrayContract;
1918
use Wrkflow\GetValue\Contracts\TransformerContract;
2019
use Wrkflow\GetValue\Contracts\TransformerStrategy;
2120
use Wrkflow\GetValue\DataHolders\AbstractData;
@@ -276,7 +275,7 @@ public function getRequiredDateTime(
276275
/**
277276
* Ensures that always an array will be returned (if missing in $data or if null).
278277
*
279-
* @param array<TransformerArrayContract>|null $transformers
278+
* @param array<TransformerContract>|null $transformers
280279
*/
281280
public function getArray(string|array $key, ?array $transformers = null): array
282281
{
@@ -299,7 +298,7 @@ public function getArray(string|array $key, ?array $transformers = null): array
299298
/**
300299
* Ensures that always an array will be returned (if missing in $data or if null).
301300
*
302-
* @param array<TransformerArrayContract>|null $transformers
301+
* @param array<TransformerContract>|null $transformers
303302
*/
304303
public function getNullableArray(string|array $key, ?array $transformers = null): ?array
305304
{
@@ -322,7 +321,7 @@ public function getNullableArray(string|array $key, ?array $transformers = null)
322321
/**
323322
* Checks if the array is in the data set with non-empty array
324323
*
325-
* @param array<TransformerArrayContract>|null $transformers
324+
* @param array<TransformerContract>|null $transformers
326325
*
327326
* @phpstan-return non-empty-array
328327
*/
@@ -340,7 +339,7 @@ public function getRequiredArray(string|array $key, ?array $transformers = null)
340339
/**
341340
* Get always `GetValue` instance even if provided data is missing or if null.
342341
*
343-
* @param array<TransformerArrayContract>|null $transformers
342+
* @param array<TransformerContract>|null $transformers
344343
*/
345344
public function getArrayGetter(string|array $key, ?array $transformers = null): self
346345
{
@@ -352,7 +351,7 @@ public function getArrayGetter(string|array $key, ?array $transformers = null):
352351
/**
353352
* Try to get nullable array from data and wrap it in `GetValue` instance.
354353
*
355-
* @param array<TransformerArrayContract>|null $transformers
354+
* @param array<TransformerContract>|null $transformers
356355
*/
357356
public function getNullableArrayGetter(string|array $key, ?array $transformers = null): ?self
358357
{
@@ -368,7 +367,7 @@ public function getNullableArrayGetter(string|array $key, ?array $transformers =
368367
/**
369368
* Checks if the array is in the data set with non-empty array
370369
*
371-
* @param array<TransformerArrayContract>|null $transformers
370+
* @param array<TransformerContract>|null $transformers
372371
*/
373372
public function getRequiredArrayGetter(string|array $key, ?array $transformers = null): self
374373
{
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValue\Transformers;
6+
7+
use Wrkflow\GetValue\Contracts\TransformerContract;
8+
use Wrkflow\GetValue\GetValue;
9+
10+
abstract class AbstractArrayItemTransformer implements TransformerContract
11+
{
12+
public function transform(mixed $value, string $key, GetValue $getValue): mixed
13+
{
14+
if (is_array($value) === false) {
15+
return null;
16+
}
17+
18+
$isAssociative = true;
19+
$items = [];
20+
$previousIndex = null;
21+
foreach ($value as $index => $item) {
22+
$result = $this->transformItem($item, $key, $index, $getValue);
23+
24+
if (is_int($index) && $isAssociative) {
25+
if ($previousIndex !== null) {
26+
$isAssociative = $previousIndex + 1 === $index;
27+
}
28+
29+
$previousIndex = $index;
30+
} else {
31+
$isAssociative = false;
32+
}
33+
34+
if ($this->ignoreNullResult() && $result === null) {
35+
continue;
36+
}
37+
38+
$items[$index] = $result;
39+
}
40+
41+
return $isAssociative ? array_values($items) : $items;
42+
}
43+
44+
abstract protected function transformItem(mixed $item, string $key, string|int $index, GetValue $getValue): mixed;
45+
46+
abstract protected function ignoreNullResult(): bool;
47+
}

src/Transformers/ArrayGetterTransformer.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@
55
namespace Wrkflow\GetValue\Transformers;
66

77
use Closure;
8-
use Wrkflow\GetValue\Contracts\TransformerArrayContract;
8+
use Wrkflow\GetValue\Contracts\TransformerContract;
99
use Wrkflow\GetValue\DataHolders\ArrayData;
1010
use Wrkflow\GetValue\GetValue;
1111

1212
/**
1313
* Transforms the value using closure after validation has been done.
1414
*/
15-
class ArrayGetterTransformer implements TransformerArrayContract
15+
class ArrayGetterTransformer implements TransformerContract
1616
{
1717
/**
18-
* @param Closure(GetValue,string):array $closure
19-
* @param bool $beforeValidation
18+
* @param Closure(GetValue,string):mixed $closure
19+
* @param bool $beforeValidation
2020
*/
2121
public function __construct(
2222
private readonly Closure $closure,
@@ -29,7 +29,7 @@ public function beforeValidation(mixed $value, string $key): bool
2929
return $this->beforeValidation;
3030
}
3131

32-
public function transform(mixed $value, string $key, GetValue $getValue): ?array
32+
public function transform(mixed $value, string $key, GetValue $getValue): mixed
3333
{
3434
if (is_array($value) === false) {
3535
return null;

src/Transformers/ArrayItemGetterTransformer.php

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,25 @@
55
namespace Wrkflow\GetValue\Transformers;
66

77
use Closure;
8-
use Wrkflow\GetValue\Contracts\TransformerArrayContract;
98
use Wrkflow\GetValue\DataHolders\ArrayData;
109
use Wrkflow\GetValue\Exceptions\NotAnArrayException;
1110
use Wrkflow\GetValue\GetValue;
1211

1312
/**
1413
* Re-build an array with array items that are wrapped within GetValue wrapper
1514
*/
16-
class ArrayItemGetterTransformer implements TransformerArrayContract
15+
class ArrayItemGetterTransformer extends AbstractArrayItemTransformer
1716
{
1817
/**
19-
* @param Closure(GetValue,string):array $onItem
20-
* @param bool $beforeValidation
18+
* @param Closure(GetValue,string):mixed $onItem
19+
* @param bool $beforeValidation
20+
* @param bool $ignoreNullResult Allows to prevent adding null value to array when
21+
* rebuilding an array.
2122
*/
2223
public function __construct(
2324
private readonly Closure $onItem,
24-
private readonly bool $beforeValidation = false
25+
private readonly bool $beforeValidation = false,
26+
private readonly bool $ignoreNullResult = true,
2527
) {
2628
}
2729

@@ -30,26 +32,19 @@ public function beforeValidation(mixed $value, string $key): bool
3032
return $this->beforeValidation;
3133
}
3234

33-
/**
34-
* @param mixed|array<array> $value
35-
*/
36-
public function transform(mixed $value, string $key, GetValue $getValue): ?array
35+
protected function transformItem(mixed $item, string $key, string|int $index, GetValue $getValue): mixed
3736
{
38-
if (is_array($value) === false) {
39-
return null;
37+
if (is_array($item) === false) {
38+
throw new NotAnArrayException($key . ' at ' . $index);
4039
}
4140

42-
$items = [];
43-
foreach ($value as $index => $item) {
44-
if (is_array($item) === false) {
45-
throw new NotAnArrayException($key . ' at ' . $index);
46-
}
47-
48-
$getItemValue = $getValue->makeInstance(new ArrayData($item, $getValue->data->getKey($key)));
41+
$getItemValue = $getValue->makeInstance(new ArrayData($item, $getValue->data->getKey($key)));
4942

50-
$items[$index] = call_user_func_array($this->onItem, [$getItemValue, $key]);
51-
}
43+
return call_user_func_array($this->onItem, [$getItemValue, $key]);
44+
}
5245

53-
return $items;
46+
protected function ignoreNullResult(): bool
47+
{
48+
return $this->ignoreNullResult;
5449
}
5550
}

0 commit comments

Comments
 (0)