-
Notifications
You must be signed in to change notification settings - Fork 775
Description
Hey, guys! The new Concrete API for Respect\Validation is almost ready then we can release our first major version (see #258). This issue was created only just to explain and discuss about what I'm working on.
There will be no changes on our Fluent API, I took care to never break the agility we have creating validation chains.
First of all I wanna be clear about the comments below, Validation is, IMHO, the best validation library ever written in PHP and I always say, @alganet is a genius for creating such an amazing library! The reason I wanna create a new Concrete API is simple, the requirements changed since the library was created, eventually code rusts and that's the normal cycle of software development.
Problems
All rules depend on AbstractRule
Yes, we have the Validatable interface, but let's be honest, we never create a rule from scratch and instead we just extend AbstractRule to create new rules.
That's a problem because all Rules are based on an implementation, if we have to change AbstractRule all rules will be changed as well.
Validatable interface has too many methods
That's the reason why we extend AbstractRule instead, this interface has 7 methods and all rule must do the same in some of them.
The methods in Validatable are really necessary to do what Validation does, I fell like all methods where created really by necessity:
assert()check($input)getName()reportError($input, array $relatedExceptions = [])setName($name)setTemplate($template)validate($input)
Decentralized validation
Each rule performs the validation to the input with no intermediate and this is a problem we are facing for more than one year (see #205 and #235).
Since we cannot intercept the input to check if it is or isn't optional, the only way to check optional inputs on all rules we have is changing all rules we have.
Currently the methods assert(), check() and __invoke() accept optional inputs as valid but the validate() method doesn't. Each rule has its own way to validate the input, so it can or cannot accept optional values and interpret whatever input as optional or valid.
NotEmpty rule
That's rule as optional value checking has been a PITA for a long time (see #244, #154, #196, #173). Also the name Empty should imply on using only the PHP empty() language constructor but it doesn't do that.
Nested exceptions
Also a problem. When using check() method it works like a charm, but wen you use assert() with key(), for example, it doesn't work properly (see #179).
I've tried several times to fix this issue on the current code-base but the fixes were always a poor workaround. Of course, working with recursion is almost always ugly, but I think we can do better on the new Concrete API.
Nested exception messages
Currently, when using assert() we can just get the full-message as string, we cannot get all messages in other format to format it as we want.
Exception message translation
We must be able to translate the exception messages in a easy way.
Cannot access Factory inside rules
Some rules depend on other rules, would be nice if we can use the same factory we already have to create them.
Proposal
I simplified the rules on a single interface with one method only:
interface RuleInterface
{
public function apply(Context $context);
}That's all we need, but you may ask me a few questions.
Why it's called apply()?
I'm opened to other names, I think it makes sense since we are talking about Rules.
What is the Context object?
Currently, a chain of validators use the Composite pattern and that's the only way to make nested validations, but we don't need to use the rules for that. The Context object uses the Composite pattern and every context has its own Rule, validation result and parameters.
The Context object allows us to define parameters to be used on exceptions, change its mode or even define the message to be displayed when an exception is thrown. By default, it defines the input to be used on some rules and also defines isValid, which indicates if the rule was successful applied or not (TRUE is the default value).
Why we don't have a $input parameter?
Firstly, because we don't need it every time, some rules just don't use the $input, secondly because Context already has the input and we can get it when we need it.
I cannot use a Rule as a single validation anymore?
That's true. I mean, you can but you have to create a Context object and then check if it isValid or not.
Any way, it will not make a difference if you use the Fluent API.
How about check(), assert(), validate() and __invoke() methods?
These methods are available on the Validator class, so you can still use them in the same way you already do it, like:
v::int()->positive()->assert($input);
v::int()->positive()->check($input);
v::int()->positive()->validate($input);
$rule = v::int()->positive();
$rule($input);The only change I've made to them: the assert() and check() methods don't return any value anymore. On our current API these methods can return just TRUE or throw an exception, so it doesn't make any sense to keep this behavior, IMO.
How do you deal with optional values?
All rules are applied by Context object.
There is an Interface called RuleRequiredInterface and any rule which implements this interface will be applied no matter if the value is optional or not (rules like NotOptional, AllOf, AlwaysInvalid, AlwaysValid). Actually, it was @qrazi's idea to create this interface (see #336).
There is a trait NotOptionalTrait which performs the validation on NotOptional rule and on Context. If the value is optional and the rule doesn't implement RuleRequiredInterface, I don't even apply the rule to the Context.
What else?
- Exception messages using Markdown by default
- The
Factoryis now accessible on all Rules byContextobject - The
RecursiveContextIteratorclass - Exceptions, Rules and Contexts are created by
Factory - PHP 5.6 or higher (HHVM and PHP 7 included), only
- Replace
{{name}}for{{placeholder}}on exception messages - Full PSR-1, PSR-2, PSR-4 (and PSR-5) support
Show me the code!
Check out the new_api branch.
PS.: Just remember it's a work in progress
Updates:
Resultwas replaced byContextisValid()method was removed