Commit graph

369 commits

Author SHA1 Message Date
Henrique Moody
562d98d805
Refactor the NotEmpty rule
Since we have the ability to use `not` as a prefix, having rules that
validate negative behaviour makes them a bit inflexible, verbose, and
harder to understand.

This commit will refactor the `NotEmpty`, and rename it to `Falsy`. It
will no longer trim strings, because Blank does a much better job at it;
it only simulates the behaviour of PHP’s native `empty()` function.

Because `Falsy`, `Blank`, and `Undef` have similar behaviour, I created
a page to demonstrate the difference and show when the user should use
one or the other.

Assisted-by: Cursor (claude-4.5-opus-high)
2025-12-29 12:48:35 +01:00
Henrique Moody
951c16e8e6
Refactor the NotEmoji rule
Since we have the ability to use `not` as a prefix, having rules that
validate negative behaviour makes them a bit inflexible, verbose, and
harder to understand.

This commit will refactor the `NotEmoji` and rename it to `Emoji`. It
will no longer check if the string contains emojis, but rather if the
string is an emoji or not. I’m also adding support to more emojis, since
the rule was a bit outdated.

This change will make the validator more strict, but will make it useful
in other scenarios. However, later on, I would like to create a rule
called `has` which, could use a validator like `Emoji` to check if the
input _has_ emojis.

Assisted-by: Cursor (claude-4.5-opus-high)
2025-12-29 11:16:25 +01:00
Henrique Moody
a37cac7142
Invert the behaviour of NoWhitespace
Since we have the ability to use `not` as a prefix, having rules that
validate negative behavior makes them a bit inflexible, verbose, and
harder to understand.

This commit will refactor the `NoWhitespace` rule by inverting its
behaviour and renaming it to `Spaced`. Although this is a breaking
change, users will still be able to have a similar behavior with the
prefix `not` + `Spaced`.
2025-12-29 09:11:01 +01:00
Henrique Moody
7046560f77
Invert the behaviour of NotUndef
Since we have the ability to use `not` as a prefix, having rules that
start with not becomes a bit inflexible, verbose, and harder to
understand.

This commit will refactor the `NotUndef` rule by inverting its behaviour
and renaming it to `Undef`.
2025-12-27 18:49:46 +01:00
Henrique Moody
338c186803
Invert the behaviour of NotBlank
Since we have the ability to use `not` as a prefix, having rules that
start with not becomes a bit inflexible, verbose, and harder to
understand.

This commit will refactor the `NotBlank` rule by inverting its behaviour
and renaming it to `Blank`. Although this is a breaking change, users
will not feel it because "NotBlank" will still be available by using the
`not` prefix followed by the `Blank` rule.
2025-12-27 17:18:30 +01:00
Henrique Moody
8e77021ce7
Reformat documentation files
There was some inconsistent Markdown, and some links were out of sync.
2025-12-27 17:03:25 +01:00
Henrique Moody
7636944ea6
Register v as a class alias to Validator
This is a pattern that we already use everywhere, so it makes sense to
simply turn `v` into a class alias to `Validator`, which will allow
people to use all the examples in the documentation without needing to
import the namespace.

We already do that when we run tests, so I just changed it to be a
global setting.
2025-12-27 00:08:35 +01:00
Henrique Moody
48405271c5
Replace placeholder "name" with "subject"
The `{{name}}` placeholder could represent different things depending on
the state of the Result, and referring to it as `{{name}}` seems
arbitrary. This commit changes it to `{{subject}}`, which is much more
generic and it describes well what that placeholder can mean.
2025-12-26 21:30:01 +01:00
Henrique Moody
8332d28acc
Do not overwrite existing names
This commit addresses the skipped tests by modifying certain core
concepts in the library. The way names work has always been somewhat
confusing, even to me. I’ve established that existing names will never
be overwritten, and if a path is already defined, we will change the
name to also include the path.

I’m not very happy with how I’m solving this problem, because the
`FirstResultStringFormatter` is changing some behaviour in the results
that seems to be something that should always happen before. But, for
the moment, I will keep it as is.
2025-12-26 14:48:23 +01:00
Henrique Moody
fd7438f9c7
Use a DI container to create instances of Validator
The `ValidatorDefaults` is cumbersome, and customising it can be
annoying. Most projects use some sort of dependency injection container,
and by integrating the creation of the `Validator` with the PSR-11, we
allow users to easily customise how they create validators.

Some tasks, like overwriting the `Translator`, become a bit more
verbose, if the user is not already using a PSR-11 container, but I
think that’s a good tradeoff.
2025-12-22 13:12:04 +01:00
Henrique Moody
7f66bcea10
Bump PHP support from 8.1 to 8.5
We want to release version 3.0 as fresh as possible, without having to
maintain backward compatibility with the previous versions. Because that
version will be on for some time, we decided it will be best to support
only PHP version 8.5 or higher.

Acked-by: Alexandre Gomes Gaigalas <alganet@gmail.com>
2025-12-18 19:03:38 +01:00
steven.lewis
2b364764f7 Extend UUID rule support to versions 1 through 8 2025-05-27 14:54:20 +01:00
Henrique Moody
94d53df8ea
Allow to use certain rules as class attributes
There are a few cases in which we want to validate the object as a
whole, and that validation could be attached to the class as a PHP
attribute. This commit enables that capability and changes a few rules
to be class attributes.
2025-01-16 10:27:16 +01:00
Henrique Moody
a0d6355980
Update some templates and improve tests
Some templates were a bit confusing, and I would like to favour adding
the `{{name}}` at the beginning of the templates as it helps when
reading nested messages.

I also deleted the regression tests for issue #1348, because it's a
non-issue, actually. The best approach to that problem is indeed using
`When` insteaf of `OneOf`.
2024-12-27 15:55:55 +01:00
Henrique Moody
a3c197f600
Handle names via the Named rule
Because of how the validation engine works now [1], there's no reason to
keep adding names to each rule. Instead, create a single rule that
handles naming rules with a few other accessories. This change is not
necessarily simple, but it shrinks the `Rule` interface, and it's more
aligned with how the library works right now.

Personally, I think this API is much more straightforward than the
`setName()` method, as it's way more explicit about which rule we're
naming. Because of this change, the behaviour changed slightly, but it's
for the best.

Because of this change, I managed to remove a lot of code, but
unfortunately, it's quite a big-bang commit. It would be too complicated
to make it atomic since names are an intrinsic part of the library.

[1]: 238f2d506a
2024-12-26 23:10:19 +01:00
Henrique Moody
1d1da7f099
Create "Templated" rule
Because of how the validation engine works, there's no reason to keep
adding templates to each rule. Instead, creating a single rule that
handles templating rules will simplify the library greatly and shrink
the `Rule` interface.

Personally, I think this API is much more straightforward than the
`setTemplate()` method, as it's way more explicit which rule is being
templated.
2024-12-26 14:40:09 +01:00
Henrique Moody
634a155c63
Always display the input
The "NotBlank", "NotEmpty", and "NotUndef" rules do not display the
input in all cases and instead displays the string "The value". The
problem with that is that one doesn't see which value was passed, which
is not so useful.

This commit will changes those rules to always display the input. If
someone doesn't want the input to show, they can always set a name for
the rule.
2024-12-22 06:55:21 +01:00
Henrique Moody
873be39105
Fix grammar error in the "AllOf" rule 2024-12-20 17:06:53 +01:00
Henrique Moody
0c07060a04
Rename "Consecutive" to "Circuit"
The "Consecutive" rule is now renamed to "Circuit" to better reflect its
behavior of stopping at the first failure. I also favour this name
because it's much shorter.
2024-12-20 16:53:56 +01:00
Dominick Johnson
243f91c3d4
Allow Min and Max to wrap chained rules
This follows the same pattern as the `Length` rule.

I've also removed the separate named and standard templates from these
rules. I didn't see a good way to implement these in the new pattern,
and also felt the language of the old standard template was somewhat
difficult to read anyway.
2024-12-20 15:08:31 +01:00
Henrique Moody
aa293dea72
Only create Length with subsequents when possible
Since I updated the validation engine[1], it became possible to create
results with subsequents[2]. This commit changes the "Length", allowing
it to create a result with a subsequent only when it's possible. That
will improve the clarity of the error messages.

[1]: 238f2d506a
[2]: 52e628fc6f
2024-12-16 21:47:53 +01:00
Henrique Moody
6e3ed793b8
Refactor Size rule
Currently, the Size rule does multiple things, yet it's limited. Because
it does many things, it's also confusing. Turning the Size rule into a
transformation allows for way more flexibility and clarity.

The syntax becomes more verbose, but the rule becomes more reusable.
2024-12-16 20:56:12 +01:00
Henrique Moody
7cec227520
Create "Attribute" rule
With this change, any rule can be used as a PHP attribute. I have wanted
to implement this feature for a while, as it allows you to bind the
validation to a specific property and just validate the object
afterwards.
2024-12-13 03:49:29 +01:00
Henrique Moody
4a16ad3d09
Allow templates to be callables
There are a few use cases in which you would like to have a custom
exception but, at the same time, reuse the message or exception that
validation might give you.

This commit creates a new feature that allows users to define a callable
that will generate an exception when it fails.
2024-12-11 17:50:47 +01:00
Henrique Moody
bba39a4bfb
Add headings to the templates 2024-12-09 01:34:50 +01:00
Henrique Moody
d5cf1311c8
Update DateTimeDiff to generate results with siblings
Since I updated the validation engine[1], it became possible to create
results with siblings. This commit changes the "DateTimeDiff", allowing
it to create a result with a sibling when possible. That will improve
the clarity of the error messages.

While at it, I noticed that we were not translating the type of
interval, so I fixed that and improved the documentation.

[1]: 238f2d506a
2024-12-09 01:31:26 +01:00
Henrique Moody
be72a54845
Add templates to each rule documentation
They only know what message a rule produces when they try running it.
Including the messages in the documentation will make it easier for
people to know what to expect.

There are a couple of undocumented placeholders, but I can live with
that for now.
2024-12-06 00:21:47 +01:00
Henrique Moody
7f1eef6bf7
Improve several validation messages
I'm not a native English speaker, and the same goes for many
contributors from this library. Because of that, some messages just
don't sound very good, so I updated a lot of them.
2024-12-05 23:37:42 +01:00
Henrique Moody
e6af762fe4
Rename "Validatable" to "Rule"
Besides the interface's name, everything already calls this type "Rule",
not "Validatable." This commit puts a stone on it and renames the
interface for better naming.
2024-12-05 19:32:14 +01:00
Henrique Moody
d356696af9
Upgrade translation mechanism
Currently, defining translations is quite cumbersome, and the translator
callback is passed to the constructor of multiple classes, which makes
it quite ugly and could make translations inconsistent.

This commit completely changes how translations are done in Validation.
Instead of using a callback, it uses a specific class, and `Validator`
will pass that object through the objects that render the messages.
2024-12-05 15:42:40 +01:00
Henrique Moody
3833ad70f8
Improve the "Feature Guide" documentation
Because the document was quite big already, I split the exception
handling part to another document.
2024-12-05 00:21:34 +01:00
Henrique Moody
eb459ad7e4
Update UndefOr to generate results with siblings
Since I updated the validation engine[1], it became possible to create
results with siblings. This commit changes the "UndefOr", allowing it to
create a result with a sibling when possible. That will improve the
clarity of the error message.

[1]: 238f2d506a
2024-12-04 19:08:32 +01:00
Henrique Moody
d1e0c8b0c1
Update NullOr to generate results with siblings
Since I updated the validation engine[1], it became possible to create
results with siblings. This commit changes the "NullOr", allowing it to
create a result with a sibling when possible. That will improve the
clarity of the error message.

I also updated the documentation, since it was still called "Nullable"

[1]: 238f2d506a
2024-12-04 18:06:12 +01:00
Henrique Moody
061a3c9c09
Rename method "validate()" to "isValid()"
The method has been renamed some time ago, but I haven't updated the
documentation to cause less confusion. Now that I want to start updating
the documentation, I switched the default branch on GitHub to the latest
stable version, so I assume there will be less confusion in the
documentation, and I can start updating the docs for the next version.
2024-12-02 22:11:57 +01:00
Henrique Moody
2aa5e39c54
Improve KeySet rule
After changes in the key-related rules, the KeySet rule became unusable.
Besides, when evaluating an input, it wasn't reporting every single
failure because it would not validate the items in the array if they had
missing or extra keys.

This commit will make several improvements to the rule. It will create
some not(keyExists($key)) rules for the extra keys, which makes the
error reporting much better. A limit of 10 additional keys will show up
when asserting an input with extra keys. I put that limit in place to
prevent the creation of too many rules.
2024-12-02 20:09:47 +01:00
Henrique Moody
de9e310f99
Deprecate age-related rules
Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-11-26 20:32:01 +01:00
Henrique Moody
cf03f8ad5f
Create "DateTimeDiff" rule
Co-authored-by: Gabriel Goulart <vieirabiel2009@hotmail.com>
2024-11-26 18:25:29 +01:00
Henrique Moody
4fd26e39bb
Rename "NotOptional" to "NotUndef"
Since I've already renamed the "Optional" rule to "UnderOf," it makes
sense to rename this rule too.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-05-06 20:45:27 +02:00
Henrique Moody
2ae1df177a
Allow to customise messages while asserting
Because we now have a single "assert()" method, we have more freedom to
add more customizations to it. This specific one is handy if someone
wants to use the library to validate but wants to use their own
exceptions.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-26 15:04:04 +01:00
Henrique Moody
707dcae65f
Refactor the "UndefOr" rule and related classes
This commit will rename the "Optional" rule to"UndefOr" while soft
deprecating the old name. It should work the same as the previous one
but with a different name. It will also prefix the result ID, allowing
more message customization.

While working on it, I realized that the prefix "undefOr" had a typo,
and it was using "undefOf" instead. I fixed that, too.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-26 01:35:36 +01:00
Henrique Moody
66faefd695
Remove previous validation engine
After many refactorings, no rules use the previous validation engine.
That means we can remove the unused code from the repository and switch
from the previous to the new validation engine everywhere.

This commit will also soft deprecate the methods "validate()", and
"check()" in all the rules and the "assert()" in all rules but the
Validator itself. That means using those methods will still be allowed,
but static analysis tools might complain.

This is a big step toward releasing the next major version, as the code
is pretty much the way it should be when I release the next version.
There's some documentation to be updated, and I would like to change the
behavior of a couple of rules.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-25 12:28:25 +01:00
Henrique Moody
9eafe52252
Refactor the Length rule
Currently, the Length rule does multiple things, yet it's limited.
Because it does many things, it's also confusing. Turning the Length
rule into a transformation allows for way more flexibility and clarity.

The syntax becomes more verbose, but I can solve that later by creating
a Transformer enables creating rules with the "length" as a prefix.

While making this change, I also removed the support for counting
objects and integers. I find that way too confusing.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-19 15:38:29 +01:00
Ville Hukkamäki
a5d042be7b
Create "Hetu" rule
This rule validates Finnish personal identity codes.

Co-authored-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-15 11:28:08 +01:00
Henrique Moody
52b75bc1ed
Create BetweenExclusive rule
Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-11 11:57:24 +01:00
Henrique Moody
2610a380dc
Replace "LazyConsecutive" with "Consecutive"
With this and the Lazy rule, the LazyConsecutive lost its purpose.

While working on it, I did refactor the Domain rule a bit, but mainly to
check how this rule could behave.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-06 23:57:49 +01:00
Henrique Moody
78715fb844
Create "Lazy" rule
This rule resembles a proper replacement for the old "KeyValue" rule
rather than the "LazyConsecutive" rule[1]. I will soon delete the
"LazyConsecutive" rule and replace it with something else.

[1]: 41245f663f

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-06 22:06:44 +01:00
Henrique Moody
24885e4a5f
Remove "KeyNested" rule
Because we have the Key and Property rules, the KeyNested is redundant,
although it's a helpful shortcut.

The real problem is dealing with messages and templates because the
structure of the validator needs to match the structure of the rule.
When using the `getMessages()` method from the exception we throw in
`assert()`, we get a flat structure, which is often not the intended
structure.

The KeyNested rule is cool, but it adds some complexity to the codebase
that I'm unwilling to deal with. It's not nice to remove a rule,
especially because I know people use it, but it's for the best. I'm
trying to keep the codebase small, so hopefully, it will get easier to
maintain.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-05 02:28:16 +01:00
Henrique Moody
d36572cc25
Split the "Property" rule
Currently, the Property rule has a third parameter that allows the
validation of the wrapped rule to be optional, meaning that the
validation will only happen if the property exists. That parameter makes
the rule harder to understand at times.

I'm splitting the Property rule into Property, PropertyExists, and
PropertyOptional. That way, it becomes apparent when someone wants only
to validate whether a property exists or if they will validate the value
of the property only when it exists.

I deliberately didn't create an abstract class because those rules are
different enough not to have an abstraction. In fact, I can see myself
deleting the AbstractRelated after I refactor the KeyNested rule.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-05 00:48:31 +01:00
Henrique Moody
a647a4737b
Split "Key" rules
Currently, the Key rule has a third parameter that allows the validation
of the wrapped rule to be optional, meaning that the validation will
only happen if the key exists. That parameter makes the rule harder to
understand at times.

I'm splitting the Key rule into Key, KeyExists, and KeyOptional. That
way, it becomes apparent when someone wants only to validate whether a
key exists or if they're going to validate the value of the key only
when it exists.

I deliberately didn't create an abstract class because those rules are
different enough not to have an abstraction. In fact, I can see myself
deleting the  "AbstractRelated" in the upcoming changes.

With these changes, the KeySet rule will not accept validating if the
key exists or validating the value only if the key exists. I should
refactor that soon, and I will likely need to create a common interface
for Key, KeyExists, and KeyOptional.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-04 00:06:18 +01:00
Henrique Moody
cea77d2a46
Recreate "Max" rule
The "Max" rule is not a transformation, validating the maximum value in
the input against a given rule.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-03 16:39:44 +01:00