问题
I am trying to create a custom symfony form validator constraint. I created two class, one constraint and one validator and it works fine. But I need to pass doctrine entitymanager instance to validator class, as I am using them standalone and not framework, I don't have yaml configuration file. I created a constructor in validator class to have $em, and in controller I have:
->add('email', EmailType::class, [
'constraints' => [
new Assert\Email(['message' => 'invalid.email', 'mode' => 'strict', 'normalizer' => 'trim']),
new Assert\EmailExists($em),
],
]);
But I am not getting $em, in my validator class, what should I do? I also tried to have constructor in main constraint class, then in validator I had parent::construct()
, still not working.
I did read this too How to configure dependencies on custom validator with Symfony Components? but instead of making the factory class, I used the current factor class and used this:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Validator\ContainerConstraintValidatorFactory;
$container = new ContainerBuilder();
$container
->register('customEmailUniqueEntity', 'EmailExistsValidator')
->addArgument($em);
$validatorBuilder = Validation::createValidatorBuilder();
$validatorBuilder->setConstraintValidatorFactory(
new ContainerConstraintValidatorFactory($container)
);
$validator = $validatorBuilder->getValidator();
$violations = $validator->validate('email address', [
new EmailExists()
]);
if (0 !== count($violations)) {
// there are errors, now you can show them
foreach ($violations as $violation) {
echo $violation->getMessage().'<br>';
}
}
With this code both dependency injection and validation works fine, but is there a trick to have this custom constraint as 'constraint' array argument within form builder rather than validating it manually?
->add('email', EmailType::class, [
'constraints' => [
new Assert\Email(['message' => 'invalid.email', 'mode' => 'strict', 'normalizer' => 'trim']),
new Assert\EmailExists($em),
],
]);
With code above I cannot pass $em to the constructor of my custom Validator. Any trick possible?
EDIT:
In order to inject doctrine EntityManager, in EmailExists
class I had:
public function validatedBy()
{
return 'customEmailUniqueEntity';
//return \get_class($this).'Validator';
}
then I had:
$container = new ContainerBuilder();
$container
->register('customEmailUniqueEntity', 'EmailExistsValidator')
->addArgument($em);
because if I was returning validator class from validatedBy()
I could not inject $em to the constructor of validator. With the answer below I used:
->addTag('validator.constraint_validator');
But now I am getting customEmailUniqueEntity
class not found error, as if I return validator from validatedBy()
, injection will not work, what should I do? I tried to return
public function validatedBy()
{
return 'EmailExists';
//return \get_class($this).'Validator';
}
but this one, of course I am getting initialize()
error. Please advise.
EDIT2:
I added addTag to:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Validator\ContainerConstraintValidatorFactory;
$container = new ContainerBuilder();
$container
->register('customEmailUniqueEntity', 'EmailExistsValidator')
->addArgument($em),
->addTag('validator.constraint_validator');
$validatorBuilder = Validation::createValidatorBuilder();
$validatorBuilder->setConstraintValidatorFactory(
new ContainerConstraintValidatorFactory($container)
);
$validator = $validatorBuilder->getValidator();
$violations = $validator->validate('email address', [
new EmailExists()
]);
if (0 !== count($violations)) {
// there are errors, now you can show them
foreach ($violations as $violation) {
echo $violation->getMessage().'<br>';
}
}
and in constructor of EmailExistsValidator I have:
var_dump($em);
and I got $em object in validator, so $em is injected and adding addTag()
did not cause any error. If I remove validatedBy()
of EmailExists
contsraint, injection will not be done. In that method I am doing
return `customEmailUniqueEntity;`
because if I return EmailExistsValidator
, injection will not be done.
Now how to use validator.constraint_validator
or EmailExists()
as constraints array param of the form? if I use new EmailExists()
I will get Two Few Aguments for validator class as $em wll not be injected this way. What to do?
回答1:
Your customEmailUniqueEntity
service will never be taken into account by the ContainerConstraintValidatorFactory
when it determines the actual validator to be used for the EmailExists
constraint.
In order to let the factory know which services are constraint validators you need to tag it with the validator.constraint_validator
tag like this:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass;
$container = new ContainerBuilder();
$container
->register('customEmailUniqueEntity', 'EmailExistsValidator')
->addArgument($em)
->addTag('validator.constraint_validator');
$container->addCompilerPass(new AddConstraintValidatorPass());
$container->compile();
回答2:
Constraints are not validators.
Symfony will take a constraint and search for its validator by attaching Validator
to the classname.
So in symfony you register your constraint by EmailExists
but the class/service which actually does validation is EmailExistsValidator
.
And this is also the place to inject EntityManagerInterface
into it.
All information can be found here: Symfony - How to Create a custom Validation Constraint
来源:https://stackoverflow.com/questions/58417417/how-to-pass-an-instance-to-symfony-form-validator-class