简体   繁体   中英

Symfony2 form validator groups without entities

I'm using Symfony2 form component to build and validate forms. Now I need to setup validator groups based on a single field value, and unfortunately it seems that every example out there is based on entities - which im not using for several reasons.

Example: If task is empty, all constraint validators should be removed, but if not, it should use the default set of validators (or a validator group).

In other words, what I'm trying to achieve is making subforms optional, but still be validated if a key field is populated.

Can someone possible give me an example how to configure it?

<?php
namespace CoreBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints as Assert;
use CoreBundle\Form\Type\ItemGroupOption;

class ItemGroup extends AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('title', 'text', array(
            'label' => 'Titel',
            'attr' => array('class' => 'span10 option_rename'),
            'required' => false
        ));
        $builder->add('max_selections', 'integer', array(
            'label' => 'Max tilvalg',
            'constraints' => array(new Assert\Type('int', array('groups' => array('TitleProvided')))),
            'attr' => array('data-default' => 0)
        ));
        $builder->add('allow_multiple', 'choice', array(
            'label' => 'Tillad flere valg',
            'constraints' => array(new Assert\Choice(array(0,1))),
            'choices'  => array(0 => 'Nej', 1 => 'Ja')
        ));
        $builder->add('enable_price', 'choice', array(
            'label' => 'Benyt pris',
            'constraints' => array(new Assert\Choice(array(0,1))),
            'choices'  => array(0 => 'Nej', 1 => 'Ja'),
            'attr' => array('class' => 'option_price')
        ));
        $builder->add('required', 'choice', array(
            'label' => 'Valg påkrævet',
            'constraints' => array(new Assert\Choice(array(0,1))),
            'choices'  => array(0 => 'Nej', 1 => 'Ja')
        ));
        $builder->add('options', 'collection', array(
            'type' => new ItemGroupOption(),
            'allow_add' => true,
            'allow_delete' => true,
            'by_reference' => false
            )
        );
        $builder->add('sort', 'hidden');
    }

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

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        global $app;

        $resolver->setDefaults(array(
            'validation_groups' => function(FormInterface $form) use ($app) {

                // Get submitted data
                $data = $form->getData();

                if (count($app['validator']->validateValue($data['title'], new Assert\NotBlank())) == 0) {
                    return array('TitleProvided');
                } else {
                    return false;
                }
            },
        ));
    }
}

If you are using 2.1 you may want to have a look at "Groups based on submitted data" .

Update

Example using the demo contact page /demo/contact in the default AcmeDemoBundle provided with Symfony Standard Edition :

The form type with conditional validation groups :

namespace Acme\DemoBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints as Assert;

class ContactType extends AbstractType
{
    // Inject the validator so we can use it in the closure
    /**
     * @var Validator
     */
    private $validator;

    /**
     * @param Validator $validator
     */
    public function __construct(Validator $validator)
    {
        $this->validator = $validator;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('email', 'email');
        $builder->add('message', 'textarea', array(
            // Added a constraint that will be applied if an email is provided
            'constraints' => new Assert\NotBlank(array('groups' => array('EmailProvided'))),
        ));
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        // This is needed as the closure doesn't have access to $this
        $validator = $this->validator;

        $resolver->setDefaults(array(
            'validation_groups' => function(FormInterface $form) use ($validator) {
                // Get submitted data
                $data = $form->getData();
                $email = $data['email'];

                // If email field is filled it will not be blank
                // Then we add a validation group so we can also check message field
                if (count($validator->validateValue($email, new Assert\NotBlank())) == 0) {
                    return array('EmailProvided');
                }
            },
        ));
    }

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

Don't forget to inject the validator service in the form type :

<?php

namespace Acme\DemoBundle\Controller;

//...

class DemoController extends Controller
{
    // ...

    public function contactAction()
    {
        $form = $this->get('form.factory')->create(new ContactType($this->get('validator')));

        // ...
    }
}

As you can see validation of the message field will be triggered only if the email field is filled.

Use a diff tool to catch the differences.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM