Skip to content

Commit 69bc6e4

Browse files
committed
Add enum support (EnumRule + getEnum/getRequiredEnum)
1 parent 1289384 commit 69bc6e4

File tree

11 files changed

+430
-7
lines changed

11 files changed

+430
-7
lines changed

docs/content/en/index.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ position: 1
1212

1313
- 🚀 Retrieve values from Array (JSON) / XML with correct return type
1414
- 🏆 Makes PHPStan / IDE happy due the return types
15-
- 🤹‍ Validation: Ensures that desired value is in correct type (without additional loop validation. Validation is always on
15+
- 🤹‍ Validation: Ensures that desired value is in correct type (without additional loop validation. Validation is always
16+
on
1617
while calling get* method).
1718
- 🛠 Transformers: Ensures that values are in expected type (ensures that string is trimmed and empty string converted to
1819
null, accepts bool as string, can be changed.)
@@ -115,7 +116,8 @@ $value = $data->getRequiredBool('key');
115116

116117
### String
117118

118-
> Throws `ValidationFailedException` if value is not string (only on non-null values).
119+
> Throws `ValidationFailedException` if value is not string (only on non-null values). In default strategy empty string
120+
> is treated as null.
119121
120122
Get nullable string value.
121123

@@ -129,6 +131,23 @@ Get required string value. Throws `MissingValueForKeyException` exception if mis
129131
$value = $data->getRequiredString('key');
130132
```
131133

134+
### Enum
135+
136+
> Throws `ValidationFailedException` if value is not in the enum. In default strategy empty string
137+
> is treated as null. Works only on string/int enums.
138+
139+
Get nullable enum value from a string/int.
140+
141+
```php
142+
$value = $data->getEnum('key', MyEnum::class);
143+
```
144+
145+
Get required enum value. Throws `MissingValueForKeyException` exception if missing.
146+
147+
```php
148+
$value = $data->getRequiredEnum('key', MyEnum::class);
149+
```
150+
132151
### Date time
133152

134153
> Throws `ValidationFailedException` if value is not string (only on non-null values).

docs/content/en/validation.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ Using [FILTER_VALIDATE_EMAIL](https://www.php.net/manual/en/filter.filters.flags
7474
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\EmailRule()]);
7575
```
7676

77+
### EnumRule
78+
79+
Checks if given value is in enum. With default strategy empty string is treated as null (not set).
80+
81+
```php
82+
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\EnumRule(MyEnum::class)]);
83+
```
84+
7785
### FloatRule
7886

7987
> Not needed if using `getFloat*` methods.

src/GetValue.php

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
namespace Wrkflow\GetValue;
66

7+
use BackedEnum;
78
use DateTime;
89
use DateTimeImmutable;
910
use DateTimeInterface;
11+
use TypeError;
12+
use UnitEnum;
1013
use Wrkflow\GetValue\Actions\GetValidatedValueAction;
1114
use Wrkflow\GetValue\Actions\ValidateAction;
1215
use Wrkflow\GetValue\Builders\ExceptionBuilder;
@@ -18,6 +21,7 @@
1821
use Wrkflow\GetValue\DataHolders\AbstractData;
1922
use Wrkflow\GetValue\DataHolders\ArrayData;
2023
use Wrkflow\GetValue\Rules\BooleanRule;
24+
use Wrkflow\GetValue\Rules\EnumRule;
2125
use Wrkflow\GetValue\Rules\NumericRule;
2226
use Wrkflow\GetValue\Rules\StringRule;
2327
use Wrkflow\GetValue\Strategies\DefaultTransformerStrategy;
@@ -128,7 +132,7 @@ public function getBool(string $key, array $rules = [], ?array $transformers = n
128132
}
129133

130134
/**
131-
* @param array<RuleContract> $rules
135+
* @param array<RuleContract> $rules
132136
* @param array<TransformerContract>|null $transformers
133137
*/
134138
public function getRequiredBool(string $key, array $rules = [], ?array $transformers = null): bool
@@ -143,7 +147,7 @@ public function getRequiredBool(string $key, array $rules = [], ?array $transfor
143147
}
144148

145149
/**
146-
* @param array<RuleContract> $rules
150+
* @param array<RuleContract> $rules
147151
* @param array<TransformerContract>|null $transformers
148152
*/
149153
public function getString(string $key, array $rules = [], ?array $transformers = null): ?string
@@ -163,7 +167,7 @@ public function getString(string $key, array $rules = [], ?array $transformers =
163167
}
164168

165169
/**
166-
* @param array<RuleContract> $rules
170+
* @param array<RuleContract> $rules
167171
* @param array<TransformerContract>|null $transformers
168172
*/
169173
public function getRequiredString(string $key, array $rules = [], ?array $transformers = null): string
@@ -178,7 +182,48 @@ public function getRequiredString(string $key, array $rules = [], ?array $transf
178182
}
179183

180184
/**
181-
* @param array<RuleContract> $rules
185+
* @template TEnum of BackedEnum
186+
* @param class-string<TEnum> $enum
187+
* @param array<RuleContract> $rules
188+
* @param array<TransformerContract>|null $transformers
189+
* @return TEnum|null
190+
*/
191+
public function getEnum(string $key, string $enum, array $rules = [], ?array $transformers = null): ?UnitEnum
192+
{
193+
$value = $this->getString(key: $key, rules: $rules + [new EnumRule($enum)], transformers: $transformers);
194+
195+
if ($value === null) {
196+
return null;
197+
}
198+
199+
//At this moment I've not found a way to detect int/string enum
200+
try {
201+
return $enum::from($value);
202+
} catch (TypeError) {
203+
return $enum::from((int) $value);
204+
}
205+
}
206+
207+
/**
208+
* @template TEnum of BackedEnum
209+
* @param class-string<TEnum> $enum
210+
* @param array<RuleContract> $rules
211+
* @param array<TransformerContract>|null $transformers
212+
* @return TEnum
213+
*/
214+
public function getRequiredEnum(string $key, string $enum, array $rules = [], ?array $transformers = null): UnitEnum
215+
{
216+
$value = $this->getEnum(key: $key, enum: $enum, rules: $rules, transformers: $transformers);
217+
218+
if ($value instanceof BackedEnum === false) {
219+
throw $this->exceptionBuilder->missingValue($key);
220+
}
221+
222+
return $value;
223+
}
224+
225+
/**
226+
* @param array<RuleContract> $rules
182227
* @param array<TransformerContract>|null $transformers
183228
*
184229
* @return DateTime|DateTimeImmutable|null
@@ -200,7 +245,7 @@ public function getDateTime(string $key, array $rules = [], ?array $transformers
200245
}
201246

202247
/**
203-
* @param array<RuleContract> $rules
248+
* @param array<RuleContract> $rules
204249
* @param array<TransformerContract>|null $transformers
205250
*
206251
* @return DateTime|DateTimeImmutable

src/Rules/EnumRule.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValue\Rules;
6+
7+
use BackedEnum;
8+
use LogicException;
9+
use TypeError;
10+
use Wrkflow\GetValue\Contracts\RuleContract;
11+
12+
class EnumRule implements RuleContract
13+
{
14+
/**
15+
* @param class-string<BackedEnum> $enum
16+
*/
17+
public function __construct(private readonly string $enum)
18+
{
19+
}
20+
21+
public function passes(mixed $value): bool
22+
{
23+
if (is_int($value) === false && is_string($value) === false) {
24+
return false;
25+
}
26+
27+
if (enum_exists($this->enum) === false) {
28+
throw new LogicException(sprintf('Provided enum <%s> is not an enum or it does not exists.', $this->enum));
29+
}
30+
31+
if (method_exists($this->enum, 'tryFrom') === false) {
32+
throw new LogicException(sprintf('Provided enum <%s> is not supported. Use string/int enum.', $this->enum));
33+
}
34+
35+
// If we are using int enum, and we will receive "string" it will throw TypeError.
36+
try {
37+
return $this->enum::tryFrom($value) !== null;
38+
} catch (TypeError) {
39+
return false;
40+
}
41+
}
42+
}

tests/AbstractArrayTestCase.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use PHPUnit\Framework\TestCase;
88
use Wrkflow\GetValue\DataHolders\ArrayData;
99
use Wrkflow\GetValue\GetValue;
10+
use Wrkflow\GetValueTests\Enums\EnumInt;
11+
use Wrkflow\GetValueTests\Enums\EnumString;
1012

1113
abstract class AbstractArrayTestCase extends TestCase
1214
{
@@ -46,6 +48,10 @@ abstract class AbstractArrayTestCase extends TestCase
4648

4749
final protected const KeyIsActiveInFalse = 'is_active_false';
4850

51+
final protected const KeyEnum = 'enum';
52+
53+
final protected const KeyEnumInt = 'enum_int';
54+
4955
protected GetValue $data;
5056

5157
protected ArrayData $arrayData;
@@ -79,6 +85,8 @@ protected function setUp(): void
7985
self::KeyIsActiveInFalse => null,
8086
self::KeyAmount => null,
8187
self::KeyUpdatedAt => null,
88+
self::KeyEnum => null,
89+
self::KeyEnumInt => null,
8290
],
8391
self::KeyEmpty => [
8492
self::KeyTags => [],
@@ -88,6 +96,8 @@ protected function setUp(): void
8896
self::KeyIsActiveInFalse => '',
8997
self::KeyAmount => '',
9098
self::KeyUpdatedAt => '',
99+
self::KeyEnum => '',
100+
self::KeyEnumInt => '',
91101
],
92102
self::KeyValid => [
93103
self::KeyTags => ['test'],
@@ -97,6 +107,8 @@ protected function setUp(): void
97107
self::KeyIsActiveInFalse => false,
98108
self::KeyAmount => 10.2,
99109
self::KeyUpdatedAt => '2022-02-02 23:22:21',
110+
self::KeyEnum => EnumString::Test->value,
111+
self::KeyEnumInt => EnumInt::Test->value,
100112
],
101113
self::KeyInvalid => [
102114
self::KeyTags => 'test',
@@ -106,6 +118,8 @@ protected function setUp(): void
106118
self::KeyIsActiveInFalse => 'not_bool',
107119
self::KeyAmount => 'not_float',
108120
self::KeyUpdatedAt => 'not_date_time',
121+
self::KeyEnum => 0,
122+
self::KeyEnumInt => 's',
109123
],
110124
self::KeyMissingValue => ['force-non-empty'],
111125
]);

tests/Enums/EnumInt.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValueTests\Enums;
6+
7+
enum EnumInt: int
8+
{
9+
case Test = 1;
10+
case Test2 = 2;
11+
}

tests/Enums/EnumOther.php

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\GetValueTests\Enums;
6+
7+
enum EnumOther
8+
{
9+
case Test;
10+
}

tests/Enums/EnumString.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Wrkflow\GetValueTests\Enums;
6+
7+
enum EnumString: string
8+
{
9+
case Test = 'test';
10+
case Test2 = 'test-2';
11+
}

0 commit comments

Comments
 (0)