简体   繁体   中英

Symfony dynamic form based on form selection

Lets say we have form where we have to choose membership type for a customer. Each type of membership has different number of co members (entity type of select fields) what can be chosen for that membership.

Now i need to implement form where based on membership type selection number of entity type of select fields will be added to form.

Membership type selection will be bade when form is already rendered to fronted.

Question is maybe too general, but how exactly i should implement this FormType ?

I have looked this coockbook entry: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data But as i understand this EventListener works during regular form rendering, so before it is displayed to frontend. Should i implement AJAX hocus pocus to form MembershipType field, so when it's chosen, whole form is rendered again ?

What must be general logic of this kind of task ?

As @keyboardSmasher mentioned in his comment a multi-step form strategy could be a good solution for you. There are plenty of ways to make this possible but lets explore a bit.

First of all, form events are not only useful prior to rendering. You can intercept the form rendering process at numerous points. You could detect that the record is a new record and only display ( or not display ) particular fields if that is the case. You could alter the newly submitted form data prior to persisting it. You can show fields only to those with the right permissions ( use a form factory as a service injecting the security context for this one ) or even show a list of friends for a user to connect with based on some criteria such as shown in the cookbook. Form events are very powerful. Further, you could also create a custom validator which checks that the chosen options are allowed for, in your case, the membership that is chosen and I highly recommend doing so ( this is very much separate from the form and should be more closely tied to your models/documents/entities/etc. )

If I understand correctly, you want to show a multiple select field containing options specific to a particular type of membership. That select field, let's call it MembershipSubOptions, is populated with options after the user selects a membership type. I hope that's correct. Be warned, the following is off the top of my head aside from a few links I googled quickly and it's very late here but it should be somewhat close to what your trying to achieve.

You can do this starting with basic ajax ( i would not recommend hocus pocus though, that stuff gets really weird ;-) ). On the client side you can use a library such as Select2 to ease population of the MembershipSubOptions via ajax. Setup a route to return options ( json ) based on the membership chosen to populate your select. Now you have a select populated with the options relevant to that membership type and, if I understand correctly, those options came from a data store so from the forms perspective it's an entity type.

I'm guessing you want the user to select multiple options from this select field and those options are likely stored as a ManyToMany relation for that user. Since we are dealing with an entity type here, we need to transform the data that is submitted so the form component knows how to deal with it. Let's create a rather empty MembershipSubOptionsType form type. It will look something like the following:

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class MembershipSubOptionsType extends AbstractType
{
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(

            )
        );
    }

    public function getParent()
    {
        return 'hidden';
    }

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

In the main form add something like the following for your sub options select field. You'll notice that I reference $this-em and pass it to the transformer. Setup your form to be managed by a factory class registered as a service and inject the Doctrine Manager so you can get the entity records you need.

$membershipSubOptionsTransformer = new MembershipSubOptionsTransformer($this->em);
$builder->add($builder->create('membershipSubOptions', new \My\GreatBundle\Form\Type\MembershipSubOptionsType(), array(
                'required'      => true, // or false if they arent required
                'label'         => 'membership.suboptions.label',
                'attr'          => array(
                    'class'     => 'membershipSubOptionsSelect',
                    'data-placeholder' => 'Select some options'
                )
            ))->addModelTransformer($membershipSubOptionsTransformer))

Now your ready to actually handle the membershipSubOptions in some way. In the select on the client side each of them is nothing more than a string or an integer but in your application they represent a real record/model/entity. The form knows to pass the submitted data to a transformer now which you will still need to build. To finish this up and not end up writing a novel, checkout the Data Transformer documentation . It's much more clear about the actual transformers than I could ever be.

Now you can dynamically populate some select field with relevant options that aren't initially provided when the form was rendered. Interestingly, the data transformer goes both ways so when your form is edited, the currently set options will be set and when they are initially submitted or updated they will be "transformed" into something your application understands.

I would set the options select field to hidden for a new record without a membership and set it to visible with js once a membership is selected. For editing forms, you'll want to use a form event ( or similar method ) to set the visibility to visible.

I hope all that makes some sense. Like I said, top of the head so YMMV but I hope that this puts you on the right path. I'm sure there are many other ways to accomplish what you want to do so I'm looking forward to reading others thoughts as well. Please let us all know what your final solution looks like so we all become better informed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM