4
votes

I'm creating a custom validator constraint to validate a "Contact", which is something like "John Doe <[email protected]>". Following the Cookbook I've created the Constraint Class:

<?php

namespace MyCompany\MyBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class Contact extends Constraint
{
    public $message = 'The string "%string%" is not a valid Contact.';
}

and also created the validator:

<?php

namespace MyCompany\MyBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\EmailValidator;

class ContactValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
        if (!preg_match('#(.*)\s+<(.*)>#', $value, $matches)) {
            $this->context->addViolation($constraint->message, array('%string%' => $value));
        }

        $emailValidator = new EmailValidator();
        if (isset($matches[2]) && $emailValidator->validate($matches[2], new Email())) {
            $this->context->addViolation($constraint->message, array('%string%' => $value));    
        }
    }
}

The point is that I'm trying to use the Symfony's EmailValidator inside my custom validator to check the email is valid. I don't want to reinvent the wheel and validate the email using my own regex.

Everything is ok when trying to validate a valid contact but, testing a contact with invalid email ("Gabriel Garcia <infoinv4l1d3mai1.com>") it craches with a PHP Fatal error:

Fatal error: Call to a member function addViolation() on a non-object in /home/dev/myproject/vendor/symfony/symfony/src/Symfony/Component/Validator/Constraints/EmailValidator.php on line 58

Digging into the EmailValidator.php class, I've realized that the issue is related to the $context (ExecutionContext). Here is line 58 of EmailValidator.php:

$this->context->addViolation($constraint->message, array('{{ value }}' => $value));

Seems that context attribute of the class is null. Anyone knows why? I need to inject it somewhere?

Thanks in advance.

P.S.: I'm using Symfony 2.3. Do not pay attention to the regex, I know it can be so much better. It's just for testing right now.

3

3 Answers

4
votes

You can directly use the constraint

see http://symfony.com/doc/current/book/validation.html

use Symfony\Component\Validator\Constraints\Email

$emailConstraint = new Email();

// use the validator to validate the value
$errorList = $this->get('validator')->validateValue(
    $email,
    $emailConstraint
);

Best regard

5
votes

I think the original question was about using EmailValidator inside a Custom Validator and in this scenario container is unavailable, so

$this->get('validator');

will not work. It seems that the only issue the poster had is to have EmailValidator addViolation to the correct context. This should work:

$emailValidator = new EmailValidator();
$emailValidator->initialize($this->context);
$emailValidator->validate($matches[2], $constraint);
2
votes

Finding this topic after have try to call custom inside custom, I made a deeply research and I may just found another better way (simpler according to me).

Valid : Sf2.6>=

$this->context->getValidator()
    ->inContext($this->context)
    ->atPath("time_on_point")
    ->validate($timeOnPoint, new AssertCustom\DateTimeRange(array('min' => '+1 day')));

In this case, I declared a new custom validator like class specific Validator and I could go directly to the field by its name. The advantage of this : I can call another custom by only applying the "new AssertCustom" and if this "AssertCustom" needs a service like a construct, I won't have a dependency because the configuration service will call all the stuff transparently.

Be careful, if you call recursively (deep) field, you will need to adapt the context according to the comments found in this file : Symfony\Component\Validator\Constraints\CollectionValidator