I'm creating a form in Symfony2. The form only contains one book
field which allows the user to choose between a list of Books
entities. I need to check whether the selected Book
belongs to an Author
I have in my controller.
public class MyFormType extends AbstractType
{
protected $author;
public function __construct(Author $author) {
$this->author = $author;
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('book', 'entity', array('class' => 'AcmeDemoBundle:Book', 'field' => 'title');
}
// ...
}
I want to check, after submitting the form, that the selected Book
is written by the $author
in my controller:
public class MyController
{
public function doStuffAction() {
$author = ...;
$form = $this->createForm(new MyFormType($author));
$form->bind($this->getRequest());
// ...
}
}
Unfortunately, I cannot find any way to do that. I tried creating a custom validator constraint as explained in The Cookbook, but while I can pass the EntityManager
as parameter by defining the validator as a service, I cannot pass the $author
from the controller to the validator constraint.
class HasValidAuthorConstraintValidator extends ConstraintValidator
{
private $entityManager;
public function __construct(EntityManager $entityManager) {
$this->entityManager = $entityManager;
}
public function validate($value, Constraint $constraint) {
$book = $this->entityManager->getRepository('book')->findOneById($value);
$author = ...; // That's the data I'm missing
if(!$book->belongsTo($author))
{
$this->context->addViolation(...);
}
}
}
This solution could be exactly the one that I was looking for, but my form is not bound to an Entity and is not meant to be (I'm getting the data from the getData()
method).
Is there a solution to my problem ? This must be a common case but I really don't know how to solve it.
I finally figured it out, with the help from Cerad. To inject custom parameters that need to be accessed from the ConstraintValidator::validate()
method, you need to pass them as options in the Constraint
.
public class HasValidAuthorConstraint extends Constraint
{
protected $author;
public function __construct($options)
{
if($options['author'] and $options['author'] instanceof Author)
{
$this->author = $options['author'];
}
else
{
throw new MissingOptionException("...");
}
}
public function getAuthor()
{
return $this->author;
}
}
And, in the ConstraintValidator:
class HasValidAuthorConstraintValidator extends ConstraintValidator
{
private $entityManager;
public function __construct(EntityManager $entityManager) {
$this->entityManager = $entityManager;
}
public function validate($value, Constraint $constraint) {
$book = $this->entityManager->getRepository('book')->findOneById($value);
$author = $this->constraint->getAuthor();
if(!$book->isAuthor($author))
{
$this->context->addViolation(...);
}
}
}
Last but not least, you have to pass the parameter to the Validator:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('book', 'entity', array(
'class' => 'AcmeDemoBundle:Book',
'field' => 'title',
'constraints' => array(
new HasValidAuthorConstraint(array(
'author' => $this->author
))
)
));
}
Start by adding a setAuthor method to your constraint and then tweaking the validate method. The trick then is to determine the best place to call it.
It's not clear exactly how you are binding the validator to your book. Are you using validation.yml or doing something inside of the form?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With