Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony Conditional Form Validation

I'm working on a Form in Symfony (2.6). The user can select a free product, which will be shipped to the user. The user has to fill in some personal details and his address (obligated). If he wants to specify another delivery address, he checks a checkbox that is not mapped to an Entity, and completes the delivery address. Now, I want to submit the form, and only validate the delivery address fields if the user has checked this checkbox. How can this be done?

The address Fields and the Delivery Address Fields use the same Form Class mapped to the same Entity. I Use a YAML-file for my constraints.

(part of) validation.yml:

AppBundle\Entity\Address:
    properties:
        street:
          - NotBlank: { message: "Please fill in your first name." }
          - Length:
              min: 3
              max: 256
              minMessage: "Please fill in your street name."
              maxMessage: "Please fill in your street name."
        number:
          - NotBlank: { message: "Please fill in your house number." }
          - Length:
              min: 1
              max: 10
              minMessage: "Please fill in your house number."
              maxMessage: "Please fill in your house number."
        postCode:
          - NotBlank: { message: "Please fill in your postal code." }
          - Length:
              min: 2
              max: 10
              minMessage: "Please fill in your postal code."
              maxMessage: "Please fill in your postal code."
        city:
          - NotBlank: { message: "Please fill in your city." }
          - Length:
              min: 2
              max: 256
              minMessage: "Please fill in your city."
              maxMessage: "Please fill in your city."
          - Type:
              type: alpha
              message: "Please fill in your city."
        country:
          - NotBlank: { message: "Please select your country." }
          - Country: ~

AppBundle\Entity\Product:
    properties:
      product:
        - NotBlank: { message: "Please select your product." }
        - Type:
            type: integer
            message: "Please select your product."
      contact:
          - Type:
              type: AppBundle\Entity\Contact
          - Valid: ~
      deliveryAddress:
          - Type:
              type: AppBundle\Entity\Address
          - Valid: ~

Product Form Class:

    <?php

class ProductFormType extends AbstractType
{

    /**
     *
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Product'
        ));
    }

    /**
     * Returns the name of this type.
     *
     * @return string The name of this type
     */
    public function getName()
    {
        return 'product';
    }


    /**
     *
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('contact', new ContactFormType()); //CONTACTFORMTYPE also has an AddressFormType for the Address Fields

        $builder->add('differentDeliveryAddress', 'checkbox', array( //delivery address is only specific for this Form
            'label'     => 'Different Shipping Address',
            'required'  => false,
            'mapped'    => false
        ));
        $builder->add('deliveryAddress', new AddressFormType());

        //specific

        $builder->add('product', 'choice', array(
            'choices' => array('a'=>'product x','b' => 'product y'),
            'required' => true,
            'invalid_message' => 'This field is required',
            'label' => 'Your Free Product',
        ));

        $builder->add('submit', 'button', array('label' => 'Submit'));
    }
}

Finally the getProductFormAction in My Controller

   public function getProductFormAction(Request $request)
{

    $product = new Product();
    $form    = $this->get('form.factory')->create(new ProductFormType($product);

    $form->handleRequest($request);

    if($form->isValid()){
        return new Response('Success'); //just to test
    }

    return $this->render(
        'productForm.html.twig',
        array(
            'pageTitle'   => 'Test',
            'form'        => $form->createView()
        )
    );
}
like image 618
N.P. Avatar asked Sep 04 '15 09:09

N.P.


1 Answers

This can relatively easily be achieved through groups.

First, add groups to the fields you only want to validate on certain occasions (your delivery address) fields.

street:
      - NotBlank: 
          message: "Please fill in your first name."
          groups: [delivery]
      - Length:
          min: 3
          max: 256
          minMessage: "Please fill in your street name."
          maxMessage: "Please fill in your street name."
          groups: [delivery]

Now that these validations are in a specific group, they will not be validated unless explicitly told to do so.

So now, we let the form determine when to validate this group.

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'validation_groups' => function (FormInterface $form) {
            $data = $form->getData();

            if ($data->differentDeliveryAddress()) {
                return array('Default', 'delivery');
            }

            return array('Default');
        },
    ));
}

Here the form will always validate the 'Default' group (all validations without any groups set), and will also validate the 'delivery' group when differentDeliveryAddress is set.

Hope this helps

like image 114
Brecht Avatar answered Oct 18 '22 02:10

Brecht