1

I have a Filter and FilterCollection object. The FilterCollection holds a collection of Filters, just like the name indicate. Now I need to validate everything, so I created a FilterType and FilterTypeCollection Forms. In the FilterCollectionType I have:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('filters', CollectionType::class, array(
            'entry_type' => FilterType::class
        ));
}

And in the FilterCollection definition I have the following:

/**
 * @var array
 * @Assert\Valid()
 */
private $filters = [];

I created a paramConverter so I could convert elements from my request into FilterCollection ones. In the apply method I try to validate everything by using:

public function apply(Request $request, ParamConverter $configuration)

    $filterCollection = new FilterCollection();

    $form = $this->formFactory->create(
        FilterTypeCollection::class,
        $filterCollection
    );

    $form->submit($request->query->all());

    if ($form->isSubmitted() && $form->isValid()) {
        $request->attributes->set($configuration->getName(), $filterCollection);

        return true;
    } else {
        throw new FormValidationException($form);
    }

}

I was expecting that the validation not only validates the FilterCollection but also the Filters. But the validations I have in my Filter definition, are not working, even if I have validations that should fail, it still passes. I think the validator is not passing on the Filter elements.

Any idea on what might be happening?

2
  • I'm not sure what's wrong with your code, but perhaps you can take a look at symfony.com/doc/current/reference/constraints/Collection.html -- hope it helps Commented Jul 10, 2017 at 8:26
  • I tried to get it to work locally and couldn't. I did find one workaround.. you can add constraints manually to the fields in your FilterForm, ex.: $builder->add('age', TextType::class, [ 'constraints' => array(new Regex(['pattern' => '/[0-9]+/'])), ]); Commented Jul 10, 2017 at 8:43

1 Answer 1

1

I finally got it to work. Perhaps you made the same mistake as me, forgetting to add "data_class" in the configureOptions in the formType.

Anyway, here's the code that works (on fresh install of Symfony 3.3)

DefaultController.php

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\Filter;
use AppBundle\Entity\FilterCollection;
use AppBundle\Form\FilterCollectionType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        // add first filter, so we don't have to implement the collection javascript etc to test quickly
        $collection = new FilterCollection();
        $collection->filters[] = new Filter();

        $form = $this->createForm(FilterCollectionType::class, $collection);
        $form->handleRequest($request);

        if ($form->isSubmitted()) {
            if ($form->isValid()) {
                echo "valid input"; // we don't want to see this ;)
            }
        }

        // replace this example code with whatever you need
        return $this->render('default/index.html.twig', [
            'form' => $form->createView()
        ]);
    }
}

Filter.php

<?php

namespace AppBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;

class Filter {
    /**
     * @var string
     * @Assert\NotBlank()
     * @Assert\Regex(pattern="/[0-9]+/")
     */
    public $name;
}

FilterCollection.php

<?php

namespace AppBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;

class FilterCollection {
    /**
     * @var Filter[]
     * @Assert\Valid()
     */
    public $filters = [];
}

FilterType.php

<?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class FilterType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name', TextType::class);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\Filter'
        ]);
    }
}

FilterCollectionType

<?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class FilterCollectionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('filters', CollectionType::class, [
            'entry_type' => FilterType::class,
        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\FilterCollection',
        ]);
    }

    public function getName()
    {
        return 'app_bundle_filter_collection_type';
    }
}

Note: I didn't make a ParamConverter like you did, but that seems beside the point of the question. You can change the code to use a ParamConverter easily.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.