3

I have a form which contains three objects:

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

Each of the embedded forms has their own validation constraints and they work. In my main form, I have cascade_validation => true so that all of the embedded form validation constraints are applied. This also works.

I am having trouble 'disabling' the validation on the billingAddress form if the sameAsShipping checkbox is enabled. I can't make the validation in the AddressType form conditional because it always needs to be enforced for the shippingAddress form.

2 Answers 2

1

I've solved this same problem by using validation groups.

First, this is important: use the validation_groups option in your AddressType to set the validation groups of every 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 different validation groups to the 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, determine determine your validation groups by looking at the value of the checkbox.

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

You can then validate only either the billingAddress or the shippingAddress, or both using the validation group mechanism.

I chose to use a button:

$formBuilder->add('submitButton', 'submit', ['validation_groups' => $checkoutValidationGroups]);
Sign up to request clarification or add additional context in comments.

Comments

1

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

/**
 * @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 is hard to understand. Read the if-condition in getGroupSequence: if not billing (address) same as shipping (address) then billing address required.

That's all, clear code in my opinion.

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.