嵌套集合类型

Nested CollectionTypes

我有一个表格 Hotel ,其中包含一些 CollectionType ,它又包含一些 CollectionType 。在这种形式中,我想添加一个父实体,它可以包含多个子实体,而子实体又可以包含多个实体:

class HotelType extends AbstractType{
/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('rooms', CollectionType::class, array(
            'entry_type' => RoomType::class,
            "label" => "Add rooms",
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,
        ))

class RoomType extends AbstractType{
/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options){
    $builder

        ->add('pictures', CollectionType::class, array(
            'entry_type' => PictureRoomType::class,
            "label" => "Add pictures",
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,
        ))
        ->add('prices', CollectionType::class, array(
            'entry_type' => PriceRoomType::class,
            "label" => "Add prices",
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,
        ))
    ;

这里是实体关系:

class Room {
/**
 * @ORM\ManyToOne(targetEntity = "Hotel", inversedBy = "rooms")
 * @ORM\JoinColumn(name = "id_hotel", referencedColumnName = "id")
 */
private $hotel;

/**
 * @ORM\OneToMany(targetEntity="PictureRoom", mappedBy="room", cascade={"remove", "persist"})
 */
private $pictures;
/**
 * @ORM\OneToMany(targetEntity="PriceRoom", mappedBy="room", cascade={"remove", "persist"})
 */
private $prices;

class Hotel {
/**
 * @ORM\OneToMany(targetEntity="Room", mappedBy="hotel", cascade={"remove", "persist"})
 */
private $rooms;

酒店管理员:

class HotelController extends Controller{
/**
 * Creates a new Hotel entity.
 *
 * @Route("/new", name="hotel_new")
 * @Method({"GET", "POST"})
 */
public function newAction(Request $request)
{
    $hotel = new Hotel();
    $form = $this->createForm('AppBundle\Form\HotelType', $hotel);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        if ($hotel->getRooms() != null) {
            foreach ($hotel->getRooms() as $room) {
                if (!empty($room)) {
                    $room->setHotel($hotel);
                    if ($room->getPictures() != null) {
                        foreach ($room->getPictures() as $picture) {
                            if (!empty($picture)) {
                                $picture->setRoom($room);
                                $picture->upload();
                            }
                        }
                    }
                    if ($room->getPrices() != null) {
                        foreach ($room->getPrices() as $price) {
                            if (!empty($price)) {
                                $price->setRoom($room);
                                $price->setIdTva($price->getIdTva()->getId());
                            }
                        }
                    }
                    $room->setIdTva($room->getIdTva()->getTva());
                }
            }
        }

我有一个处理字段追加的 JS 文件:

$(document).ready(function () {

var fields = $('div[data-prototype]');
var childElementCount = 1;

fields.each(function (index) {
    var field = $(this);
    var elementCount = 1;

    field.parent().append('<a href="#" class="btn btn-primary" id="add-another-' + index + '"><i class="glyphicon glyphicon-plus"></i> Add an element</a>');

    var trigger = $("#add-another-" + index);
    var childCollections;

    trigger.click(function (e) {
        var newWidget = field.attr('data-prototype');
        newWidget = newWidget.replace(/label__/g, "");
        newWidget = newWidget.replace(/col-md-6/g, "col-md-12");
        newWidget = newWidget.replace(/__name__/g, elementCount);
        e.preventDefault();
        elementCount++;
        field.parent().append(newWidget);

        childCollections = field.parent().find('.collection');

        if(childCollections.length == 1){

            childCollections.parent().append('<a href="#" class="btn btn-primary" id="add-another-' + index + '-'+ elementCount + '"><i class="glyphicon glyphicon-plus"></i> Add an element</a>');
            var childTrigger = $('#add-another-' + index + '-' + elementCount);

            childTrigger.click(function (e) {
                var newChildWidget = childCollections.find('div[data-prototype]').attr('data-prototype');
                //newChildWidget = newChildWidget.replace(/__child__/g, "_" + childElementCount);
                console.log(newChildWidget);
                e.preventDefault();
                childElementCount += 1;
                console.log(childElementCount);
                childCollections.parent().append(newChildWidget);
            });

        } else if (childCollections.length > 1) {
            childCollections.each(function(childIndex){
                var childField = $(this);
                childField.parent().append('<a href="#" class="btn btn-primary" id="add-another-' + index + '-'+ elementCount + childIndex + '"><i class="glyphicon glyphicon-plus"></i> Add an element</a>');
                var childTrigger = $('#add-another-' + index + '-' + elementCount + childIndex);


                childTrigger.click(function (e) {
                    var newChildWidget = childField.find('div[data-prototype]').attr('data-prototype');
                    //newChildWidget = newChildWidget.replace(/__child__/g, "_" + childElementCount);
                    console.log(newChildWidget);

                    e.preventDefault();
                    childElementCount+= 1;
                    console.log(childElementCount);
                    childField.parent().append(newChildWidget);
                });
            })
        }
    });
});

事件有效,当我单击 "Add an element" 时,正确的 FormType 附加到正确的 div。但是当我提交数据时,孙子字段(如 PictureRoom 或 PricesRoom)不会保存到数据库中。有人知道如何使用那种嵌套的 CollectionType 表单吗?

非常感谢您的帮助。

理解这一点很重要,CollectionType 是 Symfony Form Component 的一个特性,它并没有太多需要教条的地方。

因此,对于 CollectionType,您将在表单数据 ArrayCollection 中拥有一些对象,但如何处理和持久化它们 - 这完全是您的责任。

就个人而言,我更喜欢使用 empty_data 属性 来保存通过 CollectionType 创建的新实体。

有点像

   ->add('prices', CollectionType::class, array(
        'entry_type' => PriceRoomType::class,
        "label" => "Add prices",
        'allow_add' => true,
        'allow_delete' => true,
        'prototype' => true,
        'empty_data' => function (FormInterface $form) {
             $price = PriceRoom();
             $price->setRoom($form->getParent()->getData());
             return $price; 
         },
    ))

RoomPicture 相同。并且您必须在任何级别上拥有 cascade={"remove", "persist"}(要为酒店保留的房间)。

在这种情况下不确定 $form->getParent()->getData() 是否会有 Room 实体,但无论如何它可以通过某种方式从 $form 对象访问,只需玩一下代码。