Unable to use Callback assert with a form without data_class
Asked Answered
C

1

7

I'm creating a custom FormType called IntervalType. My IntervalType will have two fields, start and end and will be of type integer. This custom FormType will always be used without data_class.

I want to add a constraint to guarantee that start is lower than end.

How do I use the Symfony\Component\Validator\Constraints\Callback directly in a FormType without data_class?

Here is my IntervalType, just for reference:

// src/AppBundle/Form/Type/IntervalType.php
namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Validator\Constraints\NotBlank;

class TaskType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('start', IntegerType::class, array(
                'constraints' => array(
                    new NotBlank(),
                ),
            ))
            ->add('end', IntegerType::class, array(
                'constraints' => array(
                    new NotBlank(),
                ),
            ))
        );
    }
}
Ciera answered 21/3, 2017 at 18:30 Comment(0)
E
20

When the form won't be using any data_class the only option seems to be the Callback constraint.

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

class IntervalType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('start', IntegerType::class, array(
                'constraints' => array(
                    new NotBlank(),
                ),
            ))
            ->add('end', IntegerType::class, array(
                'constraints' => array(
                    new NotBlank(),
                    new Callback(array($this, 'validateInterval')),
                ),
            ))
            ->add('submit', SubmitType::class);
    }

    public function validateInterval($value, ExecutionContextInterface $context)
    {
        $form = $context->getRoot();
        $data = $form->getData();

        if ($data['start'] >= $value) {
            $context
                ->buildViolation('The end value has to be higher than the start value')
                ->addViolation();
        }
    }
}
Electroluminescence answered 21/3, 2017 at 20:20 Comment(3)
It works parcially. When I embedded the IntervalType inside another form, it fails, because $context->getRoot() returns the root form, so $data['start'] doesn't exists.Ciera
$form = $context->getRoot() is exactly what I needed! Thanks!Lepidote
You can alternatively use $form = $context->getObject()->getParent(); where $context->getObject() will be the FormInterface of the field the Callback assertion is assigned. This is useful when using an embedded form collection, to return the FormInterface object that contains the fields, instead of the top level (root) FormInterface.Nappy

© 2022 - 2024 — McMap. All rights reserved.