Skip to content

Commit

Permalink
Support for trimming empty values
Browse files Browse the repository at this point in the history
  • Loading branch information
mabar committed Feb 19, 2025
1 parent 5d4c111 commit 8b6561c
Show file tree
Hide file tree
Showing 18 changed files with 156 additions and 48 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased](https://github.com/orisai/object-mapper/compare/0.3.0...v1.x)

### Added

- `StringRule`
- `trim` option to remove empty characters from start and end of the string

### Changed

- Runtime meta
Expand All @@ -19,6 +24,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `ArrayShapeRule`
- Simplify finding missing fields (performance optimization)
- Reduce calls needed to find field names for "did you mean" error helper (performance optimization)
- `BoolRule`
- `castBoolLike` option trims empty characters from start and end of the string
- `FloatRule`
- `castNumericString` option trims empty characters from start and end of the string
- `IntRule`
- `castNumericString` option trims empty characters from start and end of the string
- `NullRule`
- `castEmptyString` option trims empty characters from start and end of the string
- `StringRule`
- `notEmpty` detects more empty values

### Fixed

Expand Down
9 changes: 8 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ Parameters:
- `castBoolLike`
- accepts also `0` (int|string), `1` (int|string), `'true'` (string, any case), `'false'` (string, any case)
- value is cast to respective bool value
- empty characters from start and end of a string are ignored
- default `false` - bool-like are not cast

### enum - from array rule
Expand Down Expand Up @@ -459,6 +460,7 @@ Parameters:
- `castNumericString`
- accepts also numeric strings (float and int)
- value is cast to respective float value
- empty characters from start and end of a string are ignored
- default `false` - numeric strings are not cast
- e.g. `'10.0'`, `'10'`, `'+10.0'`, `'-10.0'` (commas, spaces etc. are not supported)

Expand Down Expand Up @@ -593,6 +595,7 @@ Parameters:
- `castNumericString`
- accepts also numeric strings (int)
- value is cast to respective int value
- empty characters from start and end of a string are ignored
- default `false` - numeric strings are not cast
- e.g. `'10'`, `'+10'`, `'-10'` (commas, spaces etc. are not supported)

Expand Down Expand Up @@ -713,6 +716,7 @@ Parameters:
- `castEmptyString`
- accepts any string with only empty characters
- value is cast to null
- empty characters from start and end of a string are ignored
- default `false` - empty strings are not cast
- e.g. `''`, `' '`, `"\t"` ,`"\t\n\r""`

Expand Down Expand Up @@ -895,7 +899,7 @@ final class StringInput implements MappedObject
public string $field;

/** @var non-empty-string */
#[StringValue(minLength: 1, maxLength: 100, notEmpty: true, pattern: '/^abc/')]
#[StringValue(minLength: 1, maxLength: 100, notEmpty: true, trim: true, pattern: '/^abc/')]
public string $anotherField;

}
Expand Down Expand Up @@ -948,6 +952,9 @@ Parameters:
- string **must not** contain **only** empty characters
- default `false` - empty strings are allowed
- e.g. `''`, `' '`, `"\t"` ,`"\t\n\r""`
- `trim`
- trim empty characters from start and end of the string
- default `false`
- `pattern`
- regex pattern which must match
- default `null` - no validation by pattern
Expand Down
31 changes: 16 additions & 15 deletions src/Rules/BoolRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Orisai\ObjectMapper\Rules;

use Nette\Utils\Strings;
use Orisai\ObjectMapper\Args\Args;
use Orisai\ObjectMapper\Args\ArgsChecker;
use Orisai\ObjectMapper\Exception\ValueDoesNotMatch;
Expand Down Expand Up @@ -64,8 +65,8 @@ public function processValue(
{
$initValue = $value;

if (!is_bool($value)) {
$value = $this->tryConvert($value, $args);
if ($args->castBoolLike && !is_bool($value)) {
$value = $this->tryConvert($value);
}

if (is_bool($value)) {
Expand Down Expand Up @@ -94,22 +95,22 @@ public function createType(
* @param mixed $value
* @return mixed
*/
private function tryConvert($value, BoolArgs $args)
private function tryConvert($value)
{
if ($args->castBoolLike) {
if (is_string($value)) {
$value = strtolower($value);
}

if (
(is_string($value) || is_int($value))
&& isset(self::CastMap[$value])
) {
return self::CastMap[$value];
}
$originalValue = $value;

if (is_string($value)) {
$value = strtolower(Strings::trim($value));
}

if (
(is_string($value) || is_int($value))
&& isset(self::CastMap[$value])
) {
return self::CastMap[$value];
}

return $value;
return $originalValue;
}

}
6 changes: 5 additions & 1 deletion src/Rules/FloatRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Orisai\ObjectMapper\Rules;

use Nette\Utils\Strings;
use Orisai\Exceptions\Logic\InvalidArgument;
use Orisai\ObjectMapper\Args\Args;
use Orisai\ObjectMapper\Args\ArgsChecker;
Expand Down Expand Up @@ -180,11 +181,14 @@ public function createType(
*/
private function tryConvert(string $value)
{
$originalValue = $value;

$value = Strings::trim($value);
if (preg_match('#^[+-]?[0-9]*[.]?[0-9]+\z#', $value) === 1) {
return (float) $value;
}

return $value;
return $originalValue;
}

}
6 changes: 5 additions & 1 deletion src/Rules/IntRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Orisai\ObjectMapper\Rules;

use Nette\Utils\Strings;
use Orisai\Exceptions\Logic\InvalidArgument;
use Orisai\ObjectMapper\Args\Args;
use Orisai\ObjectMapper\Args\ArgsChecker;
Expand Down Expand Up @@ -177,11 +178,14 @@ public function createType(
*/
private function tryConvert(string $value)
{
$originalValue = $value;

$value = Strings::trim($value);
if (preg_match('#^[+-]?[0-9]+\z#', $value) === 1) {
return (int) $value;
}

return $value;
return $originalValue;
}

}
14 changes: 8 additions & 6 deletions src/Rules/NullRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Orisai\ObjectMapper\Rules;

use Nette\Utils\Strings;
use Orisai\ObjectMapper\Args\Args;
use Orisai\ObjectMapper\Args\ArgsChecker;
use Orisai\ObjectMapper\Exception\ValueDoesNotMatch;
Expand All @@ -12,7 +13,6 @@
use Orisai\ObjectMapper\Processing\Value;
use Orisai\ObjectMapper\Types\SimpleValueType;
use function is_string;
use function preg_match;

/**
* @implements Rule<NullArgs>
Expand Down Expand Up @@ -54,8 +54,8 @@ public function processValue(
DynamicContext $dynamic
)
{
if ($value !== null) {
$value = $this->tryConvert($value, $args);
if ($args->castEmptyString && $value !== null) {
$value = $this->tryConvert($value);
}

if ($value !== null) {
Expand Down Expand Up @@ -84,13 +84,15 @@ public function createType(
* @param mixed $value
* @return mixed
*/
private function tryConvert($value, NullArgs $args)
private function tryConvert($value)
{
if ($args->castEmptyString && is_string($value) && preg_match('/\S/', $value) !== 1) {
$originalValue = $value;

if (is_string($value) && Strings::trim($value) === '') {
return null;
}

return $value;
return $originalValue;
}

}
6 changes: 5 additions & 1 deletion src/Rules/StringArgs.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ final class StringArgs implements Args

public ?int $maxLength;

public bool $trim;

public function __construct(
?string $pattern,
bool $notEmpty,
?int $minLength,
?int $maxLength
?int $maxLength,
bool $trim
)
{
$this->pattern = $pattern;
$this->notEmpty = $notEmpty;
$this->minLength = $minLength;
$this->maxLength = $maxLength;
$this->trim = $trim;
}

}
20 changes: 16 additions & 4 deletions src/Rules/StringRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Orisai\ObjectMapper\Rules;

use Nette\Utils\Strings;
use Orisai\ObjectMapper\Args\Args;
use Orisai\ObjectMapper\Args\ArgsChecker;
use Orisai\ObjectMapper\Exception\ValueDoesNotMatch;
Expand All @@ -25,13 +26,14 @@ final class StringRule implements Rule
Pattern = 'pattern',
MinLength = 'minLength',
MaxLength = 'maxLength',
NotEmpty = 'notEmpty';
NotEmpty = 'notEmpty',
Trim = 'trim';

public function resolveArgs(array $args, MetaFieldContext $context): StringArgs
{
$checker = new ArgsChecker($args, self::class);

$checker->checkAllowedArgs([self::Pattern, self::NotEmpty, self::MinLength, self::MaxLength]);
$checker->checkAllowedArgs([self::Pattern, self::NotEmpty, self::MinLength, self::MaxLength, self::Trim]);

$pattern = null;
if ($checker->hasArg(self::Pattern)) {
Expand All @@ -53,7 +55,12 @@ public function resolveArgs(array $args, MetaFieldContext $context): StringArgs
$maxLength = $checker->checkNullableInt(self::MaxLength);
}

return new StringArgs($pattern, $notEmpty, $minLength, $maxLength);
$trim = false;
if ($checker->hasArg(self::Trim)) {
$trim = $checker->checkBool(self::Trim);
}

return new StringArgs($pattern, $notEmpty, $minLength, $maxLength, $trim);
}

public function getArgsType(): string
Expand All @@ -78,9 +85,14 @@ public function processValue(
throw ValueDoesNotMatch::create($this->createType($args, $services, $dynamic), Value::of($value));
}

$trimmedValue = null;
if ($args->trim) {
$trimmedValue = $value = Strings::trim($value);
}

$invalidParameters = [];

if ($args->notEmpty && preg_match('/\S/', $value) !== 1) {
if ($args->notEmpty && ($trimmedValue ?? Strings::trim($value)) === '') {
$invalidParameters[] = self::NotEmpty;
}

Expand Down
7 changes: 6 additions & 1 deletion src/Rules/StringValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,21 @@ final class StringValue implements RuleDefinition

private bool $notEmpty;

private bool $trim;

public function __construct(
?string $pattern = null,
?int $minLength = null,
?int $maxLength = null,
bool $notEmpty = false
bool $notEmpty = false,
bool $trim = false
)
{
$this->pattern = $pattern;
$this->minLength = $minLength;
$this->maxLength = $maxLength;
$this->notEmpty = $notEmpty;
$this->trim = $trim;
}

public function getType(): string
Expand All @@ -48,6 +52,7 @@ public function getArgs(): array
StringRule::MinLength => $this->minLength,
StringRule::MaxLength => $this->maxLength,
StringRule::NotEmpty => $this->notEmpty,
StringRule::Trim => $this->trim,
];
}

Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Rules/ArrayOfRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public static function provideResolveValid(): Generator
],
new ArrayOfArgs(
new RuleRuntimeMeta(ScalarRule::class, new EmptyArgs()),
new RuleRuntimeMeta(StringRule::class, new StringArgs(null, false, null, null)),
new RuleRuntimeMeta(StringRule::class, new StringArgs(null, false, null, null, false)),
1,
10,
true,
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Rules/ArrayShapeRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public static function provideResolveValid(): Generator
],
new ArrayShapeArgs([
1 => new RuleRuntimeMeta(ScalarRule::class, new EmptyArgs()),
'foo' => new RuleRuntimeMeta(StringRule::class, new StringArgs(null, false, null, null)),
'foo' => new RuleRuntimeMeta(StringRule::class, new StringArgs(null, false, null, null, false)),
]),
];
}
Expand Down
2 changes: 2 additions & 0 deletions tests/Unit/Rules/BoolRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ public function provideBoolLikeValues(): Generator
yield ['fAlSe', false];
yield [0, false];
yield ['0', false];
yield ["\t 1 \n", true];
yield [' false ', false];
}

/**
Expand Down
3 changes: 3 additions & 0 deletions tests/Unit/Rules/FloatRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ public function provideFloatLikeValues(): Generator
yield ['100', 100.0];
yield ['100.12', 100.12];
yield ['-100', -100.0];
yield ['-100.5', -100.5];
yield ["\n\t -100.5 \t", -100.5];
yield [' -100.5 ', -100.5];
}

/**
Expand Down
2 changes: 2 additions & 0 deletions tests/Unit/Rules/IntRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ public function provideIntLikeValues(): Generator
yield ['+10', 10];
yield ['100', 100];
yield ['-100', -100];
yield ["\n\t -100 \t", -100];
yield [' -100 ', -100];
}

/**
Expand Down
1 change: 1 addition & 0 deletions tests/Unit/Rules/NullRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public function provideNullLike(): Generator
yield [''];
yield [' '];
yield [' '];
yield ["\n\t"];
}

/**
Expand Down
Loading

0 comments on commit 8b6561c

Please sign in to comment.