如何在多个字段上使用FormEvents动态修改表单

时间:2016-12-21 11:03:15

标签: php symfony symfony-forms

关于symfony doc on form events,我试图在用户更改其他字段时动态激活/停用或设置多个字段的值...

FormType

<?php

namespace AppBundle\Form;

use AppBundle\Entity\ConsumptionMode;
use AppBundle\Entity\Operation;
use AppBundle\Entity\PaymentMode;
use AppBundle\Entity\Service;
use AppBundle\Entity\User;
use AppBundle\Repository\ServiceRepository;
use AppBundle\Repository\UserRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * Class OperationType
 * @package AppBundle\Form
 */
class OperationType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     * @throws \Symfony\Component\Form\Exception\InvalidArgumentException
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        //default displayed form inputs
        //the user must select a service to enable other fields
        //the quantity and the dates will not change on the service selection
        //the user list, the price and the billable fields will change on service selection
        $builder
            //HERE IS THE FIRST FIELD I WANT TO LISTEN TO
            ->add('service', EntityType::class, [
                'class' => Service::class,
                'choice_label' => 'name',
                'placeholder' => '-- Choose a service --',
                'query_builder' => function(ServiceRepository $sr) {
                    return $sr->createQueryBuilder('s')
                        ->where('s.enabled = true');
                }
            ])
            ->add('quantity', IntegerType::class)
            ->add('consumptionMode', ChoiceType::class, [
                'choices' => ConsumptionMode::AVAILABLE_MODES
            ])
            //HERE IS THE SECOND FIELD I WANT TO LISTEN TO
//            ->add('paymentMode', ChoiceType::class, [
//                'choices' => PaymentMode::AVAILABLE_MODES,
//                'required' => false
//            ])
            ->add('dateStart', DateType::class, ['required' => false])
            ->add('dateEnd', DateType::class, ['required' => false])
            ->add('dateEndPostponed', DateType::class, ['required' => false])
            //allow dynamic add / delete of sub-operations
            ->add('children', CollectionType::class, [
                'label' => 'Sub-Operations',
                'entry_type' => SubOperationType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
                'prototype_name' => '__sub_operation_index__'
            ]);

        //add listener to change the default values when loading the form
        $builder->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'onPresetData']);
        //add listener to change the default values when the user select a service
        $builder->get('service')->addEventListener(FormEvents::POST_SUBMIT, [$this, 'onPostSubmit']);

        //HERE IS THE SECOND FIELD LISTENER
//        //add listener to change the enable extra qtity and price values when the user select a payment mode
//        $builder->get('paymentMode')->addEventListener(FormEvents::POST_SUBMIT, [$this, 'onPostSubmit']);
    }

    /**
     * @param FormEvent $event
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    public function onPresetData(FormEvent $event)
    {
        //get the closure that will add and initialise the user/price/billable fields
        $formModifier = $this->getFormModifier();

        //Note: As we use this form as a "prototype" in Package & RFOP,
        //The event->getData will return null on the prototype creation (i.e onPreSetData event)
        $service = $event->getData() !== null ? $event->getData()->getService() : null;

        //call the closure with the event
        $formModifier($event->getForm(), $service);
    }

    /**
     * @param FormEvent $event
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    public function onPostSubmit(FormEvent $event)
    {
        //get the closure that will add and initialise the user/price/billable/... fields
        $formModifier = $this->getFormModifier();

        // It's important here to fetch $event->getForm()->getData(), as
        // $event->getData() will get the client data (that is, the ID)
        $service = $event->getForm()->getData();

        // since we've added the listener to the child, we'll have to pass on
        // the parent to the callback functions!
        $formModifier($event, $event->getForm()->getParent(), $service);
    }

    /**
     * @param OptionsResolver $resolver
     * @throws \Symfony\Component\OptionsResolver\Exception\AccessException
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Operation::class
        ]);
    }

    /**
     * Get the managers linked to the Service::defaultAllocationTeam
     *
     * @param Service $service
     * @return User[]
     */
    private function getDefaultTeamUserMangers(Service $service)
    {
        $users = [];
        $userTeams = $service->getDefaultAllocationTeam()->getUserTeams();
        foreach($userTeams as $userTeam) {
            $user = $userTeam->getUser();
            //TODO Maybe use the authorizationChecker to do this ?
            //FIXME declare Roles constants in User entity
            //only add the managers
            if ($user->hasRole('ROLE_MANAGER')) {
                //add user
                $users[] = $user;
            }
        }

        return $users;
    }

    /**s
     * add the users field in the form
     * preset the data using the selected service
     *
     * @param FormInterface $form
     * @param Service|null $service
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    private function addUserField(FormInterface $form, Service $service = null)
    {
        //default user field options
        $options = [
            'multiple' => true,
            'class' => User::class,
            'disabled' => true,//disable the field by default
            'choice_label' => 'email',
            'query_builder' => function (UserRepository $ur) {
                return $ur->createQueryBuilder('u')
                    ->where('u.enabled = true')
                    ->orderBy('u.email', 'ASC');
            }
        ];

        //enable user field when the user selected a service
        if ($service !== null) {
            //get the preferred users (managers) for the service (using the defaultAllocationTeam service field)
            $defaultTeamUsers = $this->getDefaultTeamUserMangers($service);

            //enable the user field and set preferred choices
            $options = array_merge($options, [
                'disabled' => false,
                'preferred_choices' => function($user) use ($defaultTeamUsers) {
                    return in_array($user, $defaultTeamUsers, true);
                }
            ]);
            //TODO preset users
        }

        //create the users field
        $form->add('users', EntityType::class, $options);
    }

    /**
     * Add price field
     * Preset the price value using the service defaultUnitPrice field
     *
     * @param FormInterface $form
     * @param Service|null $service
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    private function addPriceField(FormInterface $form, Service $service = null)
    {
        $options = [
            'currency' => 'EUR',
            'required' => false,
            'disabled' => true
        ];

        //enable the price field
        if ($service !== null) {
            $options = array_merge($options, [
                'disabled' => false,
                //set the price in the form
                'empty_data' => $service->getDefaultUnitPrice()
            ]);
        }

        //create the form field
        $form->add('unitPrice', MoneyType::class, $options);
    }

    /**
     * Add billable field
     * Preset the isBillable value using the Service::isBillable function
     *
     * @param FormInterface $form
     * @param Service|null $service
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    private function addBillableField(FormInterface $form, Service $service = null)
    {
        $options = [
            'label' => 'Billable ?',
            'required' => false,
            'disabled' => true
        ];

        if ($service !== null) {
            //check billable checkbox only if isBillable is true
            if ($service->isBillable()) {
                //only add the empty_data option if the service is billable
                //note: if we add 'empty_data' with false, the checkbox will still be checked
                $options['empty_data'] = true;
            }

            //enable the billable field
            $options = array_merge($options, [
                'disabled' => false,
            ]);
        }

        //create the form field
        $form->add('billable', CheckboxType::class, $options);
    }

    /**
     * Add ExtraAllowedQuantity field
     * Activate this field only if the user set PAY_AS_YOU paymentMode
     *
     * @param FormInterface $form
     * @param null|string $paymentMode
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     */
    private function addExtraAllowedQuantityField(FormInterface $form, string $paymentMode = null)
    {
        $options = [
            'required' => false,
            'disabled' => true
        ];

        //enable the extraAllowedQuantity field only when PAY_AS_YOU_GO is selected
        if ($paymentMode === PaymentMode::PAY_AS_YOU_GO) {
            $options['required'] = true;
            $options['disabled'] = false;
        }

        $form->add('extraAllowedQuantity', IntegerType::class, $options);
    }

    /**
     * Add ExtraPrice field
     * Activate this field only if the user selected PAY_AS_YOU_GO paymentMode
     *
     * @param FormInterface $form
     * @param string|null $paymentMode
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     */
    private function addExtraPriceField(FormInterface $form, string $paymentMode = null)
    {
        $options = [
            'currency' => 'EUR',
            'required' => false,
            'disabled' => true
        ];

        //enable the extraAllowedQuantity field only when PAY_AS_YOU_GO is selected
        if ($paymentMode === PaymentMode::PAY_AS_YOU_GO) {
            $options['required'] = true;
            $options['disabled'] = false;
        }

        $form->add('extraPrice', MoneyType::class, $options);
    }

    /**
     * @return \Closure
     * @throws \OutOfBoundsException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     */
    private function getFormModifier()
    {
        return function (FormInterface $form, Service $data = null) {
            //Note: As we use this form as a "prototype" in Package & RFOP,
            //The event->getData will return null on the prototype creation (i.e onPreSetData event)

            //add the user field to the form
            $this->addUserField($form, $data);
            //add the billable field to the form
            $this->addBillableField($form, $data);
            //add the price field to the form
            $this->addPriceField($form, $data);

            //HERE IS WHAT I WANT TO ADD FOR THE PAYMENT_MODE FIELD

//            //add the extra price field to the form
//            $this->addExtraPriceField($form, $data);
//            //add the extra quantity field to the form
//            $this->addExtraAllowedQuantityField($form, $data);
        };
    }
}

此时,service字段上的监听器完全按预期运行(表单加载了users / unitPrice字段已禁用 ...当用户选择了serviceusersunitPrice字段已启用并使用服务默认信息进行设置。)

我需要做的是在paymentMode字段上添加另一个侦听器。 首先,表单会将extraAllowedQtity / extraPrice加载为禁用不需要;当用户选择PAY_AS_YOU_GO paymentMode时,系统会启用并需要此字段。

Behavior on loading /add page(请参阅评论,了解选择服务时的行为)

所以pre_set_data监听器将初始化所有字段,据我所知,post_submit将处理表单字段的修改......

事实是,当我提交服务时,会触发paymentMode postSubmit侦听器......

我很确定我错过了关于form_event系统的一些内容,但我不知道是什么。

注意:我知道我应该从formType中获取监听器,但是在成功完成FormType中的工作之后我会这样做。

1 个答案:

答案 0 :(得分:0)

我设法做了我想做的事,如果它可以提供帮助,这就是代码:

<?php

namespace AppBundle\Form;

use AppBundle\Entity\ConsumptionMode;
use AppBundle\Entity\Operation;
use AppBundle\Entity\PaymentMode;
use AppBundle\Entity\Service;
use AppBundle\Entity\User;
use AppBundle\Repository\ServiceRepository;
use AppBundle\Repository\UserRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * Class OperationType
 * @package AppBundle\Form
 */
class OperationType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     * @throws \Symfony\Component\Form\Exception\InvalidArgumentException
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        //default displayed form inputs
        //the user must select a service to enable other fields
        //the quantity and the dates will not change on the service selection
        //the user list, the price and the billable fields will change on service selection
        $builder
            ->add('service', EntityType::class, [
                'required' => true,
                'class' => Service::class,
                'choice_label' => 'name',
                'placeholder' => '-- Choose a service --',
                'query_builder' => function(ServiceRepository $sr) {
                    return $sr->createQueryBuilder('s')
                        ->where('s.enabled = true');
                }
            ])
            ->add('billable', CheckboxType::class, [
                'label' => 'Billable ?',
                'required' => false,
                'disabled' => true
            ])
            ->add('unitPrice', MoneyType::class, [
                'currency' => 'EUR',
                'required' => false,
                'disabled' => true
            ])
            ->add('quantity', IntegerType::class, ['required' => true])
            ->add('consumptionMode', ChoiceType::class, [
                'placeholder' => '-- Choose a consumption mode --',
                'required' => false,
                'choices' => ConsumptionMode::AVAILABLE_MODES
            ])
            ->add('paymentMode', ChoiceType::class, [
                'choices' => PaymentMode::AVAILABLE_MODES,
                'required' => false,
                'placeholder' => '-- Choose a payment mode --'
            ])
            ->add('extraAllowedQuantity', IntegerType::class, [
                'required' => false,
                'disabled' => true
            ])
            ->add('extraPrice', MoneyType::class, [
                'currency' => 'EUR',
                'required' => false,
                'disabled' => true
            ])
            ->add('dateStart', DateType::class, ['required' => false])
            ->add('dateEnd', DateType::class, ['required' => false])
            ->add('dateEndPostponed', DateType::class, ['required' => false])
            ->add('users', EntityType::class, [
                'multiple' => true,
                'class' => User::class,
                'disabled' => true,//disable the field by default
                'choice_label' => 'email',
                'query_builder' => function (UserRepository $ur) {
                    return $ur->createQueryBuilder('u')
                        ->where('u.enabled = true')
                        ->orderBy('u.email', 'ASC');
                }
            ])
            //allow dynamic add / delete of sub-operations
            ->add('children', CollectionType::class, [
                'label' => 'Sub-Operations',
                'entry_type' => SubOperationType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
                'prototype_name' => '__sub_operation_index__'
            ]);

        //add listener to change the default values when loading the form
        $builder->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'onPresetData']);
        //add listener to change the default values when the user select a service
        $builder->get('service')->addEventListener(FormEvents::POST_SUBMIT, [$this, 'onPostSubmitService']);
        //add listener to change the enable extra qtity and price values when the user select a payment mode
        $builder->get('paymentMode')->addEventListener(FormEvents::POST_SUBMIT, [$this, 'onPostSubmitPaymentMode']);
    }

    /**
     * @param FormEvent $event
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    public function onPresetData(FormEvent $event)
    {
        $formModifier = $this->getFormModifier();

        //Note: As we use this form as a "prototype" in Package & RFOP,
        //The event->getData will return null on the prototype creation (i.e onPreSetData event)
        $service = $event->getData() === null ? null : $event->getData()->getService();
        $paymentMode = $event->getData() === null ? null : $event->getData()->getPaymentMode();

        //call the closure with the event
        $formModifier($event->getForm(), $service, $paymentMode);
    }

    /**
     * @param FormEvent $event
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    public function onPostSubmitService(FormEvent $event)
    {
        //get the closure that will add and initialise the user/price/billable/... fields
        $formModifier = $this->getFormModifier();

        // It's important here to fetch $event->getForm()->getData(), as
        // $event->getData() will get the client data (that is, the ID)

        // since we've added the listener to the child, we'll have to pass on
        // the parent to the callback functions!
        $formModifier($event->getForm()->getParent(), $event->getForm()->getData());
    }

    /**
     * @param FormEvent $event
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    public function onPostSubmitPaymentMode(FormEvent $event)
    {
        //get the closure that will add and initialise the user/price/billable/... fields
        $formModifier = $this->getFormModifier();

        // It's important here to fetch $event->getForm()->getData(), as
        // $event->getData() will get the client data (that is, the ID)

        // since we've added the listener to the child, we'll have to pass on
        // the parent to the callback functions!
        $formModifier($event->getForm()->getParent(), null, $event->getForm()->getData());
    }

    /**
     * @param OptionsResolver $resolver
     * @throws \Symfony\Component\OptionsResolver\Exception\AccessException
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Operation::class
        ]);
    }

    /**
     * Get the managers linked to the Service::defaultAllocationTeam
     *
     * @param Service $service
     * @return User[]
     */
    private function getDefaultTeamUserMangers(Service $service)
    {
        $users = [];
        $userTeams = $service->getDefaultAllocationTeam()->getUserTeams();
        foreach($userTeams as $userTeam) {
            $user = $userTeam->getUser();
            //TODO Maybe use the authorizationChecker to do this ?
            //FIXME declare Roles constants in User entity
            //only add the managers
            if ($user->hasRole('ROLE_MANAGER')) {
                //add user
                $users[] = $user;
            }
        }

        return $users;
    }

    /**s
     * add the users field in the form
     * preset the data using the selected service
     *
     * @param FormInterface $form
     * @param Service|null $service
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    private function addUserField(FormInterface $form, Service $service = null)
    {
        //default user field options
        $options = $form->get('users')->getConfig()->getOptions();

        //enable user field when the user selected a service
        if ($service !== null) {
            //get the preferred users (managers) for the service (using the defaultAllocationTeam service field)
            $defaultTeamUsers = $this->getDefaultTeamUserMangers($service);

            //enable the user field and set preferred choices
            $options = array_merge($options, [
                'disabled' => false,
                'preferred_choices' => function($user) use ($defaultTeamUsers) {
                    return in_array($user, $defaultTeamUsers, true);
                }
            ]);
            //TODO preset users

            //create the users field
            $form->add('users', EntityType::class, $options);
        }
    }

    /**
     * Add price field
     * Preset the price value using the service defaultUnitPrice field
     *
     * @param FormInterface $form
     * @param Service|null $service
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    private function addPriceField(FormInterface $form, Service $service = null)
    {
        $options = $form->get('unitPrice')->getConfig()->getOptions();

        //enable the price field
        if ($service !== null) {
            $options = array_merge($options, [
                'disabled' => false,
                //set the price in the form
                'empty_data' => $service->getDefaultUnitPrice()
            ]);

            $form->add('unitPrice', MoneyType::class, $options);
        }
   }

    /**
     * Add billable field
     * Preset the isBillable value using the Service::isBillable function
     *
     * @param FormInterface $form
     * @param Service|null $service
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    private function addBillableField(FormInterface $form, Service $service = null)
    {
        //get the default options of the
        $options = $form->get('billable')->getConfig()->getOptions();

        if ($service !== null) {
            //check billable checkbox only if isBillable is true
            if ($service->isBillable()) {
                //only add the empty_data option if the service is billable
                //note: if we add 'empty_data' with false, the checkbox will still be checked
                $options['empty_data'] = true;
            }

            //enable the billable field
            $options = array_merge($options, [
                'disabled' => false,
            ]);

            //create the form field
            $form->add('billable', CheckboxType::class, $options);
        }
    }

    /**
     * Add ExtraAllowedQuantity field
     * Activate this field only if the user set PAY_AS_YOU paymentMode
     *
     * @param FormInterface $form
     * @param null|string $paymentMode
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    private function addExtraAllowedQuantityField(FormInterface $form, string $paymentMode = null)
    {
        $options = $form->get('extraAllowedQuantity')->getConfig()->getOptions();

        //enable the extraAllowedQuantity field only when PAY_AS_YOU_GO is selected
        if ($paymentMode === PaymentMode::PAY_AS_YOU_GO) {
            $options['required'] = true;
            $options['disabled'] = false;

            $form->add('extraAllowedQuantity', IntegerType::class, $options);
        }
    }

    /**
     * Add ExtraPrice field
     * Activate this field only if the user selected PAY_AS_YOU_GO paymentMode
     *
     * @param FormInterface $form
     * @param string|null $paymentMode
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \OutOfBoundsException
     */
    private function addExtraPriceField(FormInterface $form, string $paymentMode = null)
    {
        $options = $form->get('extraPrice')->getConfig()->getOptions();

        //enable the extraAllowedQuantity field only when PAY_AS_YOU_GO is selected
        if ($paymentMode === PaymentMode::PAY_AS_YOU_GO) {
            $options['required'] = true;
            $options['disabled'] = false;

            $form->add('extraPrice', MoneyType::class, $options);
        }
    }

    /**
     * @return \Closure
     * @throws \OutOfBoundsException
     * @throws \Symfony\Component\Form\Exception\UnexpectedTypeException
     * @throws \Symfony\Component\Form\Exception\LogicException
     * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException
     */
    private function getFormModifier()
    {
        return function (FormInterface $form, Service $service = null, string $paymentMode = null) {
            //Note: As we use this form as a "prototype" in Package & RFOP,
            //The event->getData will return null on the prototype creation (i.e onPreSetData event)

            //add the user field to the form
            $this->addUserField($form, $service);
            //add the billable field to the form
            $this->addBillableField($form, $service);
            //add the price field to the form
            $this->addPriceField($form, $service);
            //add the extra price field to the form
            $this->addExtraPriceField($form, $paymentMode);
            //add the extra quantity field to the form
            $this->addExtraAllowedQuantityField($form, $paymentMode);
        };
    }
}
  

请记住,您应该将侦听器提取到某些事件中   订阅者以获得更易读的FormType