stixx
stixx

Reputation: 142

Symfony making part of a form optional on submit

I'm currently having trouble with shutting down some validation constraints if a certain option is selected in the form. This option is not related to the model and is not set using the data_class option.

My form contains two embedded address forms which are exactly the same as in one invoice address and one shipment address. If the alternative option is selected, I want to make the shipment address form required through validation. If no alternative option is selected the shipment address form requires no validation and needs to be left alone.

CustomerCheckoutForm

class CustomerCheckoutForm extends AbstractType
{

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('id', 'hidden')
        ->add('firstName', 'text')
        ->add('nameAdditions', 'text')
        ->add('lastName', 'text')
        ->add('gender', 'choice', array('expanded' => true, 'choices' => array('m' => 'M', 'f' => 'F')))
        ->add('birthDate', 'date', array(
            'required' => true,
            'input' => 'string',
            'widget' => 'choice',
            'years' => range(DATE_CURRENT_YEAR - 80, DATE_CURRENT_YEAR - 18),
            'empty_value' => array('year' => 'Year', 'month' => 'Month', 'day' => 'Day')
        ))
        ->add('invoiceAddress', new AddressForm($options['countryMapper']), array(
            'label' => false,
            'required' => true,
        ))
        ->add('alternative_shipment_address', 'choice', array(
            'expanded' => true,
            'choices' => array(0 => 'Delivery on the current address', 1 => 'Alternative shipping address'),
            'mapped' => false,
            'required' => true,
            'label' => 'Delivery address',
            'data' => 0,
        ))
        ->add('shipmentAddress', new AddressForm($options['countryMapper']), array(
            'label' => false,
            'required' => false,
        ))
        ->add('Continue', 'submit');

    $this->registerListeners($builder);
}

private function registerListeners(FormBuilderInterface $builder)
{
    $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
        $customer = $event->getData();
        $form = $event->getForm();

        if (!$customer || $customer->getId() === null) {
            $form->add('password', 'password');
        }
    });
}

/**
 * {@inheritdoc}
 */
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'EHV\SharedBundle\Model\Customer',
        'countryMapper' => null,
        'validation_groups' => array(
            'Default',
            'checkout',
        ),
    ));
}

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

}

AddressForm

class AddressForm extends AbstractType
{

private $countryMapper;

public function __construct(CountryMapper $countryMapper)
{
    $this->countryMapper = $countryMapper;
}

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $transformer = new CountryToIdTransformer($this->countryMapper);
    $countries = $this->countryMapper->getAll();
    $data[''] = 'Make a choice...';

    /** @var Country $country */
    foreach ($countries as $country) {
        $data[$country->getId()] = $country->getName();
    }

    $builder
        ->add('id', 'hidden')
        ->add('streetName', 'text')
        ->add('streetNumber', 'text')
        ->add('streetNumberAddition', 'text')
        ->add('postalCode', 'text')
        ->add('city', 'text')
        ->add(
            $builder->create('country', 'choice', array('choices' => $data))
                ->addModelTransformer($transformer)
        );
}

/**
 * {@inheritdoc}
 */
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'EHV\SharedBundle\Model\Address',
    ));
}

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

}

Both models have their own constraints set through the function:

public static function loadValidatorMetadata(ClassMetaData $metadata) {}

Upvotes: 2

Views: 280

Answers (2)

stixx
stixx

Reputation: 142

I managed to use a Form event listener with the Constraint Callback answered by Hpatoio, details below.

CustomerCheckoutForm

    $builder->addEventListener(FormEvents::SUBMIT, function(FormEvent $event) {
        $form = $event->getForm();
        $customer = $event->getData();

        if ($form->get('alternative_shipment_address')->getData() === 0) {
            $customer->setShipmentAddress(null);
        }
    });

CustomerConstraint

    $callback = function($customer) use ($metadata) {
        $metadata->addPropertyConstraint('invoiceAddress', new Valid());

        /** @var Customer $customer */
        if ($customer->getShipmentAddress() !== null) {
            $metadata->addPropertyConstraint('shipmentAddress', new Valid());
        }
    };

    $metadata->addConstraint(new Callback($callback));

Credits to Hpatoio for pointing out the right direction.

Upvotes: 0

Hpatoio
Hpatoio

Reputation: 1815

Checkout Callback constraints http://symfony.com/doc/current/reference/constraints/Callback.html

You can read the value of your addresses and rise error if needed.

Upvotes: 1

Related Questions