Symfony: Forms with inherited classes

﹥>﹥吖頭↗ 提交于 2019-12-12 03:06:28

问题


I'm trying to figure out how to handle forms when using inherited class types with Symfony (2.8.6).

I have created a [very] simple example case of what I'm trying to do below. There are problems with it, but it's only to illustrate my question.

  1. How can I supply just one form going from the controller to a twig template so that there can be a field to choose what "type" (discriminator) should be used? Should I simply create another variable, such as "type" that is hardcoded in each class?
  2. Once a form is submitted, how can I figure out which class should be used in the controller, in either a "new" or "edit" action? I've tried plucking it out of the ParameterBag, creating an appropriate entity and form, then using $form->handleRequest($request); ...but it doesn't seem to work when there are extra fields that might belong to another type.

If someone could even point me to a Github repo or something that shows something like this happening, I'd be very grateful. Thanks for your time.

If these are my classes:

 /**
 * @ORM\Entity
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap("truck" = "Truck", "Car" = "Car", "suv" = "SUV")
 */
abstract class Vechicle {
    private $make;
    private $model;
    private $numberOfDoors;

    // getters and setters //
}

class Truck extends Vehicle {
    private $offRoadPackage;
    private $bedSize;

    // getters and setters //
}

class Car extends Vehicle {
    private $bodyType;
}

class SUV extends Vehicle {
    // no additional fields //
}

then something like these would be my form types:

class VehicleType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
            ->add('make')
            ->add('model')
            ->add('numberOfDoors');
    }

    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults(array(
            'data_class' => 'MyBundle\Entity\Vehicle'
        ));
    }
}

class TruckType extends VehicleType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        parent::buildForm($builder, $options);
        $builder
            ->add('offRoadPackage')
            ->add('bedSize');
    }

    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults(array(
            'data_class' => 'MyBundle\Entity\Truck'
        ));
    }
}

class CarType extends VehicleType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        parent::buildForm($builder, $options);
        $builder
            ->add('bodyType')
    }

    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults(array(
            'data_class' => 'MyBundle\Entity\Car'
        ));
    }
}

class SUVType extends VehicleType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        parent::buildForm($builder, $options);
    }

    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults(array(
            'data_class' => 'MyBundle\Entity\SUV'
        ));
    }
}

回答1:


This is going to be a bit of a lengthy one but bear with me. The gist of this idea is that you deal with your forms in an array. You create a list of types which you iterate to build the actual form objects. This way the only thing you edit is the list of form types if you wish to add more.

In your template you iterate over all the forms to render them and wrap them in a div you can hide. Next you can add a select element that controls a piece of javascript showing/hiding the form of the type the user has selected.

After sbumission you can test if the action has been POSTed to and reiterate the forms to check which one of them has been submitted and handle it appropriately.

Below is a crude untested code snippet:

The controller/action:

class SomeController
{
    public function addAction()
    {
        $types = [
            'Form1' => Form1::class,
            'Form2' => Form2::class,
            'Form3' => Form3::class,
        ];

        // create the forms based on the types indicated in the types arary
        $forms = [];
        foreach ($types as $type) {
            $forms[] = $this->createForm($type);
        }

        if ($request->isMethod('POST')) {
            foreach ($forms as $form) {
                $form->handleRequest($request);

                if (!$form->isSubmitted()) continue; // no need to validate a form that isn't submitted

                if ($form->isValid()) {
                    // handle the form of your type

                    break; // stop processing as we found the form we have to deal with
                } 
            }
        }

        $views = [];
        foreach ($forms as $form) {
            $views = $form->createView();
        }

        $this->render('template.html.twig', ['forms' => $views, 'types' => $types]);
    }

}

The template:

<select id="types">
    {% for type in types|keys %}
        <option value="vehicle_type_{{ loop.index }}">{{ type }}</option>
    {% endfor %}
</select>
{% for form in forms %}
    <div class="form hidden" id="vehicle_type_{{ loop.index }}">
        {{ form_start(form) }}
        {{ form_widget(form) }}
        {{ form_end(form) }}
    </div>
{% endfor %}

And finally the piece of javascript controlling what form is shown/hidden:

<script>
    // On select change hide all forms except for the on that was just selected
    $('#types').on('change', function () {
        $('.form').addClass('hidden');
        $('#' + $(this).val()).removeClass('hidden');
    });
</script>


来源:https://stackoverflow.com/questions/37488773/symfony-forms-with-inherited-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!