8000 [RFC][Validator] Produce better trace when there are no violations · Issue #59662 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content
[RFC][Validator] Produce better trace when there are no violations #59662
Open
@valtzu

Description

@valtzu

Description

While it is super easy to get the reasons why validation failed, it's impossible to get any proof/trace of all the things that were validated. Not even TraceableValidator helps here, because the information (code, message) is not exposed from ConstraintValidatorInterface implementations unless there are violations. The most you can do today is save all the properties of existing constraints to logs (or have custom logic per Constraint to form similar messages as Validator) – but it would be much nicer if we could have the same messages as we do for violations. The violation messages in built-in Constraints are written in a way which does not say "This value is less than 10" but "This value should be at least 10", meaning they could be used as-is when validation passes too.

With this it would be possible to simply use Validator to validate business rules and generate (with a little bit of user-space code) audit logs like

✅ age: This value should be between 18 and 100. 
❌ internalCustomerScore: This value should be between 150.0 and 250.0.

Then later when you need to know "was the age of a particular customer checked 3 years ago" and "what were the age limits at the time", it is really simple to do by just checking the logs originated from the Validator.

Drawbacks / challenges

  1. This would likely be really bad for performance & memory usage, thus it would need to be opt-in to store the non-violation-related data
  2. BC break(s), though we could still keep ValidatorInterface untouched
  3. A lot of work, since there are so many ConstraintValidator implementations

Why Validator, it was not built for this!

  1. Developers are familiar with it, no need to come up or familiarize with a custom solution
  2. Unlike some php business logic (ifs etc), Validator Constraints are declarative – it is possible to inspect and enumerate them, generate docs
  3. Having something separate, yet really similar to Validator would mean a lot of maintenance work as all the related validation logic would need to be separately maintained. You'd basically end-up copy-pasting the ConstraintValidators and only add the exposing of non-violation results

Example

Below is an example from the built-in RangeValidator, demonstrating the problem and possible solution.

-        if ($hasLowerLimit && $hasUpperLimit && ($value < $min || $value > $max)) {
+        if ($hasLowerLimit && $hasUpperLimit) {
             $message = $constraint->notInRangeMessage;
             $code = Range::NOT_IN_RANGE_ERROR;

-            $violationBuilder = $this->context->buildViolation($message)
+            $resultBuilder = $this->context->buildValidationResult($value < $min || $value > $max)
+                ->setMessage($message)
                 ->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE))
                 ->setParameter('{{ min }}', $this->formatValue($min, self::PRETTY_DATE))
                 ->setParameter('{{ max }}', $this->formatValue($max, self::PRETTY_DATE))
                 ->setCode($code);

             if (null !== $constraint->maxPropertyPath) {
-                $violationBuilder->setParameter('{{ max_limit_path }}', $constraint->maxPropertyPath);
+                $resultBuilder->setParameter('{{ max_limit_path }}', $constraint->maxPropertyPath);
             }

             if (null !== $constraint->minPropertyPath) {
-                $violationBuilder->setParameter('{{ min_limit_path }}', $constraint->minPropertyPath);
+                $resultBuilder->setParameter('{{ min_limit_path }}', $constraint->minPropertyPath);
             }

-            $violationBuilder->addViolation();
+            $resultBuilder->addResult();
         }

And then there would be some ExecutionContextInterface::getResults() method to fetch all the results. This could co-exist with the existing getViolations().

Metadata

Metadata

Assignees

No one assigned

    Labels

    RFCRFC = Request For Comments (proposals about features that you want to be discussed)Validator

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0