Skip to content

Commit b0fbc08

Browse files
committed
Create "Formatted" validator
The Formatted validator decorates another validator to transform how input values appear in error messages, while still validating the original unmodified input. This is useful for improving the readability of error messages by displaying values in a user-friendly formatd. The validator accepts any Respect\StringFormatter\Formatter implementation, allowing direct use of StringFormatter's fluent builder. As StringFormatter expands with more formatters in future releases, users will automatically benefit from the full range of formatting options. Assisted-by: Claude Code (Opus 4.5)
1 parent b701fac commit b0fbc08

17 files changed

Lines changed: 253 additions & 2 deletions

File tree

docs/migrating-from-v2-to-v3.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ Version 3.0 introduces several new validators:
585585
| `BetweenExclusive` | Validates that a value is between two bounds (exclusive) |
586586
| `ContainsCount` | Validates the count of occurrences in a value |
587587
| `DateTimeDiff` | Validates date/time differences (replaces Age validators) |
588+
| `Formatted` | Formats input values in error messages |
588589
| `ShortCircuit` | Stops at first failure instead of collecting all errors |
589590
| `Hetu` | Validates Finnish personal identity codes (henkilötunnus) |
590591
| `KeyExists` | Checks if an array key exists |
@@ -659,6 +660,20 @@ v::dateTimeDiff('years', v::greaterThanOrEqual(18))->assert('2000-01-01'); // pa
659660
v::dateTimeDiff('days', v::lessThan(30))->assert('2024-01-15'); // passes if less than 30 days ago
660661
```
661662

663+
#### Formatted
664+
665+
Decorates a validator to format input values in error messages while still validating the original input:
666+
667+
```php
668+
use Respect\StringFormatter\FormatterBuilder as f;
669+
670+
v::formatted(f::mask('1-4'), v::email())->assert('not an email');
671+
// → "****an email" must be an email address
672+
673+
v::formatted(f::pattern('#### #### #### ####'), v::creditCard())->assert('1234123412341234');
674+
// → "1234 1234 1234 1234" must be a credit card number
675+
```
676+
662677
#### ShortCircuit
663678

664679
Validates input against a series of validators, stopping at the first failure. Useful for dependent validations:

docs/validators.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ In this page you will find a list of validators by their category.
2727

2828
**Date and Time**: [Date][] - [DateTime][] - [DateTimeDiff][] - [LeapDate][] - [LeapYear][] - [Time][]
2929

30-
**Display**: [Format][] - [Masked][] - [Named][] - [Templated][]
30+
**Display**: [Format][] - [Formatted][] - [Masked][] - [Named][] - [Templated][]
3131

3232
**File system**: [Directory][] - [Executable][] - [Exists][] - [Extension][] - [File][] - [Image][] - [Mimetype][] - [Readable][] - [Size][] - [SymbolicLink][] - [Writable][]
3333

@@ -53,7 +53,7 @@ In this page you will find a list of validators by their category.
5353

5454
**Structures**: [Attributes][] - [Key][] - [KeyExists][] - [KeyOptional][] - [KeySet][] - [Property][] - [PropertyExists][] - [PropertyOptional][]
5555

56-
**Transformations**: [After][] - [All][] - [Each][] - [Length][] - [Max][] - [Min][] - [Size][]
56+
**Transformations**: [After][] - [All][] - [Each][] - [Formatted][] - [Length][] - [Max][] - [Min][] - [Size][]
5757

5858
**Types**: [ArrayType][] - [ArrayVal][] - [BoolType][] - [BoolVal][] - [CallableType][] - [Countable][] - [FloatType][] - [FloatVal][] - [IntType][] - [IntVal][] - [IterableType][] - [IterableVal][] - [NullType][] - [NumericVal][] - [ObjectType][] - [ResourceType][] - [ScalarVal][] - [StringType][] - [StringVal][]
5959

@@ -118,6 +118,7 @@ In this page you will find a list of validators by their category.
118118
- [FloatType][] - `v::floatType()->assert(1.5);`
119119
- [FloatVal][] - `v::floatVal()->assert(1.5);`
120120
- [Format][] - `v::format(f::pattern('00-00'))->assert('42-33');`
121+
- [Formatted][] - `v::formatted(f::mask('1-4'), v::email())->assert('foo@example.com');`
121122
- [Graph][] - `v::graph()->assert('LKM@#$%4;');`
122123
- [GreaterThan][] - `v::greaterThan(10)->assert(11);`
123124
- [GreaterThanOrEqual][] - `v::intVal()->greaterThanOrEqual(10)->assert(10);`
@@ -275,6 +276,7 @@ In this page you will find a list of validators by their category.
275276
[FloatType]: validators/FloatType.md "Validates whether the type of the input is float."
276277
[FloatVal]: validators/FloatVal.md "Validate whether the input value is float."
277278
[Format]: validators/Format.md "Validates whether an input is already formatted as the result of applying a provided"
279+
[Formatted]: validators/Formatted.md "Decorates a validator to format input values in error messages while still validating the original input."
278280
[Graph]: validators/Graph.md "Validates if all characters in the input are printable and actually creates"
279281
[GreaterThan]: validators/GreaterThan.md "Validates whether the input is greater than a value."
280282
[GreaterThanOrEqual]: validators/GreaterThanOrEqual.md "Validates whether the input is greater than or equal to a value."

docs/validators/Formatted.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!--
2+
SPDX-License-Identifier: MIT
3+
SPDX-FileCopyrightText: (c) Respect Project Contributors
4+
SPDX-FileContributor: Henrique Moody <henriquemoody@gmail.com>
5+
-->
6+
7+
# Formatted
8+
9+
- `Formatted(Formatter $formatter, Validator $validator)`
10+
11+
Decorates a validator to format input values in error messages while still validating the original input.
12+
13+
```php
14+
use Respect\StringFormatter\FormatterBuilder as f;
15+
16+
v::formatted(f::mask('1-4'), v::email())->assert('foo@example.com');
17+
// Validation passes successfully
18+
19+
v::formatted(f::mask('1-4'), v::email())->assert('not an email');
20+
// → "****an email" must be an email address
21+
22+
v::formatted(f::pattern('#### #### #### ####'), v::creditCard())->assert('4111111111111111');
23+
// Validation passes successfully
24+
25+
v::formatted(f::pattern('#### #### #### ####'), v::creditCard())->assert('1234123412341234');
26+
// → "1234 1234 1234 1234" must be a credit card number
27+
```
28+
29+
This validator is useful for displaying formatted values in error messages, making them more readable for end users. For example, showing credit card numbers with spaces or phone numbers with proper formatting.
30+
31+
It uses [respect/string-formatter](https://github.com/Respect/StringFormatter) as the underlying formatting engine. See the [StringFormatter documentation](https://github.com/Respect/StringFormatter) for available formatters.
32+
33+
## Behavior
34+
35+
The validator first ensures the input is a valid string using `StringVal`. If the input passes string validation, it validates the original input using the inner validator. The formatted version is only used for display in error messages.
36+
37+
## Categorization
38+
39+
- Display
40+
- Transformations
41+
42+
## Changelog
43+
44+
| Version | Description |
45+
| ------: | :---------- |
46+
| 3.0.0 | Created |
47+
48+
## See Also
49+
50+
- [Masked](Masked.md)
51+
- [Named](Named.md)
52+
- [Templated](Templated.md)

src-dev/Commands/LintMixinCommand.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ final class LintMixinCommand extends Command
111111
'PropertyExists',
112112
'PropertyOptional',
113113
'Attributes',
114+
'Formatted',
114115
'Templated',
115116
'Named',
116117
];

src/Mixins/Builder.php

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Mixins/Chain.php

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Mixins/NotBuilder.php

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Mixins/NotChain.php

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Mixins/NullOrBuilder.php

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Mixins/NullOrChain.php

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)