Symfony 3多级嵌套表单

5
我一直在学习Symfony 3教程中有关嵌入表单集合的embedding,我想将其扩展到更深层次的嵌套。我查看了一些资料,虽然Symfony 2有部分答案,但并不全面(而且没有针对3的)。
如果我们以教程中Task有多个Tag为例,那么如何编写代码使其扩展到:Task有多个Tag,每个Tag都有多个SubTag
到目前为止,我认为我理解了表单类:

Task:

class TaskType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('description');

        $builder->add('tags', CollectionType::class, array(
            'entry_type' => TagType::class,
            'allow_add' => true,
            'by_reference' => false,
            'allow_delete' => true
        ));
    }

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

标签:

class TagType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('name');

            $builder->add('sub_tags', CollectionType::class, array(
                'entry_type' => SubTagType::class,
                'allow_add' => true,
                'by_reference' => false,
                'allow_delete' => true
            ));
        }

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

子标签:

class SubTagType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('name');
        }

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

还有基本的Twig类:

{{ form_start(form) }}
    {# render the task's only field: description #}
    {{ form_row(form.description) }}

    <h3>Tags</h3>
    <ul class="tags">
        {# iterate over each existing tag and render its only field: name #}
        {% for tag in form.tags %}
            <li>{{ form_row(tag.name) }}</li>
            {% for sub_tag in tag.sub_tags %}
                <li>{{ form_row(sub_tag.name) }}</li>
            {% endfor %}
        {% endfor %}
    </ul>
{{ form_end(form) }}

但是此时我不确定原型和JavaScript如何工作。有人能解释一下我该如何进行下一步吗?这是否是正确的方法?
我的第一个想法是,如果我们要添加其他级别,可能最好将JS泛化为任意数量的级别,因为教程使用的JS仅适用于单个级别。
我能找到的最接近可行的代码是这个堆栈溢出答案here。然而,它似乎没有像描述的那样工作,我很难弄清楚问题出在哪里。

嘿 - 如果你遇到了我无法在回答中解决的特定问题,请告诉我。(我看到你曾经将其标记为“已接受”。) - Cameron Hurd
由于某些原因,它被拒绝接受了! - Darkstarone
1个回答

3

这并不比普通的嵌套表单集合有任何不同。

但是,如果你想避免默认的__NAME__原型与父表单的原型字符串发生冲突,你应该选择不同的值作为TagTypeSubTag类型。

来自关于CollectionType的Symfony文档条目

prototype_name

  • 类型:字符串 默认值:name
  • 如果您的表单中有几个集合,或者更糟糕的是,嵌套集合,您可能希望更改占位符,以便不相关的占位符不被替换为相同的值。

如果你想使用javascript来抽象克隆操作(如本文所述),这会非常有用。顺便说一下,这篇文章似乎针对的是Symfony3!

你可能希望将传递给prototype_name的相同值作为集合持有者html的属性包含在内,以便在对data-prototype html执行replace时可以动态访问它。
var $collectionHolder;

// setup an "add a tag" link
var $addTagLink = $('<a href="#" class="add_tag_link">Add a tag</a>');
var $newLinkLi = $('<li></li>').append($addTagLink);

jQuery(document).ready(function() {
// Get the ul that holds the collection of tags
$collectionHolder = $('ul.tags');

// add the "add a tag" anchor and li to the tags ul
$collectionHolder.append($newLinkLi);

// count the current form inputs we have (e.g. 2), use that as the new
// index when inserting a new item (e.g. 2)
$collectionHolder.data('index', $collectionHolder.find(':input').length);

$addTagLink.on('click', function(e) {
    // prevent the link from creating a "#" on the URL
    e.preventDefault();

    // add a new tag form (see next code block)
    addTagForm($collectionHolder, $newLinkLi);
});

function addTagForm($collectionHolder, $newLinkLi) {
    // Get the data-prototype explained earlier
    var prototype = $collectionHolder.data('prototype');

    // get the new index
    var index = $collectionHolder.data('index');

    // Replace '__name__' in the prototype's HTML to
    // instead be a number based on how many items we have
    var newForm = prototype.replace(/__name__/g, index);

    // increase the index with one for the next item
    $collectionHolder.data('index', index + 1);

    // Display the form in the page in an li, before the "Add a tag" link li
    var $newFormLi = $('<li></li>').append(newForm);
    $newLinkLi.before($newFormLi);
}

哈哈 - 有什么我可以做来赢回你的青睐吗? - Cameron Hurd
@Cameron,你还在吗?我正在面对你所解决的问题。我不确定你建议如何编写JS代码。我该如何编写“添加子标签”的部分?谢谢。 - mario
嘿,@mario - 我还在这里。如果你能将问题缩小到更具体的内容,我很乐意提供帮助! - Cameron Hurd
嘿@Cameron,感谢回复。在这个例子中,您如何编写JS来插入/克隆子标记的表单?我能够编写适用于多个标记的表单和适用于多个子标记的表单;但我无法编写一个唯一的表单,您可以在其中创建更多的标记,并在每个标记中创建更多的子标记。我的理解是您需要混合您的代码,但我不确定如何做到这一点。(我已花费了数小时的时间研究此问题,可能要等到周四) - mario

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接