Symfony Embedded Form Conditional Validation

I have a form that contains three objects:

$builder
    ->add('customer', new CustomerType())
    ->add('shippingAddress', new AddressType())
    ->add('billingAddress', new AddressType())
    ->add('sameAsShipping', 'checkbox', ['mapped' => false])
;

      

Each of the built-in forms has their own validation constraints, and they work. In my main form I have cascade_validation => true

, so all the built-in form validation constraints apply. This also works.

I'm having a problem "disabling" validation on a form billingAddress

if a checkbox is sameAsShipping

enabled. I cannot make the validation on the AddressType conditional because it always needs to be applied to the shippingAddress form.

+3


source to share


2 answers


I solved this same problem using validation groups.

First, this is important: use the parameter validation_groups

in AddressType

to set the validation groups for each constraint of each field in the type:

<?php

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Form\FormBuilderInterface;

class AddressType extends \Symfony\Component\Form\AbstractType
{
    function buildForm(FormBuilderInterface $builder, array $options)
    {
        $groups = $options['validation_groups'];

        $builder->add('firstName', 'text', ['constraints' => new Assert\NotBlank(['groups' => $groups])]);
        $builder->add('lastName', 'text', ['constraints' => new Assert\NotBlank(['groups' => $groups])]);
    }
}

      

Then, in the parent form, pass the different validation groups into two fields:

<?php

$formBuilder = $this->get('form.factory')
    ->createNamedBuilder('checkout', 'form', null, [
        'cascade_validation' => true,
    ])
    ->add('billingAddress', 'address', [
        'validation_groups' => 'billingAddress'
    ])
    ->add('shippingAddress', 'address', [
        'validation_groups' => 'shippingAddress'
    ]);

      



Then define your validation groups by looking at the checkbox value.

if ($request->request->get('sameAsShipping')) {
    $checkoutValidationGroups = ['Default', 'billingAddress'];
} else {
    $checkoutValidationGroups = ['Default', 'billingAddress', 'shippingAddress'];
}

      

Then you can check only either billingAddress

, or shippingAddress

, or both using the check group mechanism.

I decided to use a button:

$formBuilder->add('submitButton', 'submit', ['validation_groups' => $checkoutValidationGroups]);

      

0


source


Create a form model (I use it in almost every form, but this code is not tested here):

/**
 * @Assert\GroupSequenceProvider()
 */
class YourForm implements GroupSequenceProviderInterface {
  /**
   * @Assert\Valid()
   */
  private $customer;

  /**
   * @Assert\Valid()
   */
  private $shippingAddress;

  /**
   * @Assert\Valid(groups={'BillingAddressRequired'})
   */
  private $billingAddress;

  private $billingSameAsShipping;

  public function getGroupSequence() {
    $groups = ['YourForm'];

    if(!$this->billingSameAsShipping) {
      $groups[] = 'BillingAddressRequired';
    }

    return $groups;
  }
}

      



Try to use meaningful names. sameAsShipping

hard to understand. Read the if-condition in getGroupSequence

: if there is no billing (address), same as shipping (address), then the billing address must be specified.

That's all, clear code in my opinion.

0


source







All Articles