Symfony validation - set the property path in custom validation act

Viewed 2216

I would like to ask, how to set the property path on the constraint violation.

So far, i'm doing a normal validation like (where user is a normal Entity with some predefined constraints - which are working as aspected):

$validationErrors = $this->validator->validate($user);

Next, in the user registration controller method, i'm trying to do a manual approach (because putting the constraint directly inside the user entity would cause validation on the password each time)

I'm validating the password when needed like this:

$passwordLength = mb_strlen($password);
$passwordMinLength = new Assert\Length(null, 8);
$passwordErrors = $this->validator->validate($passwordLength, [ $passwordMinLength ]);

And after that, i'm adding the result to the existing collection:

$validationErrors->addAll($passwordErrors);

But when i inspect the violation generated by the password, theres no propertyPath defined on it. I build a custom twig extension, which relies on the propertypath being set. How can i tell it to use the propertyPath called 'password' on the $passwordErrors array?

I hope you can understand my question :).

Best regards!

Rob

EDIT: I found the for me working workaround solution. I'm sure, that this is not the optimal way for solving my problem, but as i already wasted a few hours for this little thing, i decided to rather pick the workaround instead of working more hours on this little thing.

My workaround solution As i wanted to set the propertyPath for each violation though i couldn't access the private property propertyPath, i solved it in the following way:

At first i created a closure function:

$closure = Closure::bind(function (ConstraintViolation $violation) {
    $violation->propertyPath = 'password';
}, null, ConstraintViolation::class);

And then i called it for each violation:

foreach ($passwordErrors as $violation) {
    // $violation->propertyPath = 'password';
    $closure($violation);
}
2 Answers

If I understand correctly, you need a Class Constraint Validator.
You need to add this to the Constraint class:

public function getTargets()
{
    return self::CLASS_CONSTRAINT;
}

And with this, the validator’s validate() method gets an object as its first argument and the atPath() method is used to define the property which the validation error is associated to.

class ProtocolClassValidator extends ConstraintValidator
{
    public function validate($protocol, Constraint $constraint)
    {
        if ($protocol->getFoo() != $protocol->getBar()) {
            $this->context->buildViolation($constraint->message)
                ->atPath('foo')
                ->addViolation();
        }
    }
}

Documentation: https://symfony.com/doc/current/validation/custom_constraint.html

I just found out how to modify existing violations Instead of using

$validationErrors->addAll($passwordErrors);

In your custom Validator, use this

/** @var ConstraintViolationInterface $violation */
foreach ($validationErrors as $violation) {
    $this->context->buildViolation($violation->getMessage())
        ->atPath('password')
        ->addViolation();
}
Related