Symfony: формы с унаследованными классами

Я пытаюсь понять, как обрабатывать формы при использовании унаследованных типов классов с Symfony (2.8.6).

Я создал [очень] простой пример того, что я пытаюсь сделать ниже. Есть проблемы с этим, но это только для иллюстрации моего вопроса.

  1. Как я могу предоставить только одну форму, идущую от контроллера, к шаблону ветки, чтобы можно было поле для выбора того, какой "тип" (дискриминатор) следует использовать? Должен ли я просто создать другую переменную, такую ​​как "тип", которая жестко закодирована в каждом классе?
  2. Как только форма отправлена, как я могу определить, какой класс должен использоваться в контроллере, в действии "новый" или "редактировать"? Я попытался извлечь его из ParameterBag, создать соответствующую сущность и форму, а затем использовать $form->handleRequest($request); ... но это не работает, когда есть дополнительные поля, которые могут принадлежать другому типу.

Если бы кто-то мог даже указать мне на репозиторий Github или что-то, что показывает нечто подобное, я был бы очень благодарен. Спасибо за ваше время.

Если это мои занятия:

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

1 ответ

Решение

Это будет немного длинно, но терпите меня. Суть этой идеи в том, что вы имеете дело с вашими формами в массиве. Вы создаете список типов, которые вы повторяете для создания реальных объектов формы. Таким образом, единственное, что вы редактируете, это список типов форм, если вы хотите добавить больше.

В вашем шаблоне вы перебираете все формы, чтобы отобразить их, и оберните их в div, который вы можете скрыть. Затем вы можете добавить элемент select, который управляет частью JavaScript, показывая / скрывая форму выбранного пользователем типа.

После sbumission вы можете проверить, было ли действие размещено на POST и повторить формы, чтобы проверить, какая из них была отправлена ​​и обрабатывать его соответствующим образом.

Ниже приведен пример необработанного кода:

Контроллер / действие:

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, управляющий тем, какая форма отображается / скрывается:

<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>
Другие вопросы по тегам