Symfony:具有继承 类 的表单

Symfony: Forms with inherited classes

我正在尝试弄清楚在 Symfony (2.8.6) 中使用继承的 class 类型时如何处理表单。

我已经创建了一个[非常]简单的示例案例来说明我在下面尝试做的事情。虽然有问题,但只是为了说明我的问题。

  1. 我如何只提供从控制器到树枝模板的 一个 表单,以便可以有一个字段来选择 "type" (鉴别器)应该是什么用过的?我是否应该简单地创建另一个变量,例如在每个 class 中硬编码的 "type"?
  2. 提交表单后,我如何确定控制器中应该使用哪个 class,在 "new" 或 "edit" 操作中?我试过将它从 ParameterBag 中取出来,创建一个合适的实体和表单,然后使用 $form->handleRequest($request); ...但是当存在可能属于另一种类型的额外字段时,它似乎不起作用。

如果有人甚至可以向我指出 Github 存储库或显示类似情况发生的内容,我将不胜感激。谢谢你的时间。

如果这些是我的class:

 /**
 * @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 //
}

那么我的表单类型就是这些:

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'
        ));
    }
}

这会有点冗长,但请耐心等待。这个想法的要点是你在一个数组中处理你的表单。您创建了一个类型列表,您可以对其进行迭代以构建实际的表单对象。这样,如果您想添加更多,您唯一编辑的就是表单类型列表。

在您的模板中,您遍历所有表单以呈现它们并将它们包装在您可以隐藏的 div 中。接下来,您可以添加一个 select 元素来控制 javascript showing/hiding 用户拥有的类型的形式 selected.

发送完成后,您可以测试该操作是否已发布到并重复表单以检查其中一个已提交并适当处理。

下面是一个粗略的未经测试的代码片段:

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]);
    }

}

模板:

<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 %}

最后 javascript 块控制什么形式 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>