Я работал над учебным курсом по Symfony 3 вложения коллекция форм, и я хочу распространить идею на дополнительные вложенные уровни. Я осмотрелся, и есть частичные ответы для Symfony 2, но ничего комплексного (и ничего для 3).
Если мы возьмем учебники Task
имеет много Tag
Например, как бы я закодировал это так, чтобы оно распространялось на: Task
имеет много Tag
имеет много SubTag
?
До сих пор я думаю, что я понимаю классы формы:
Задача:
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',
));
}
}
Subtag:
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, который может работать только на одном уровне.
Ближайший рабочий код, который я могу найти, — это ответ о переполнении стека. Вот. Тем не менее, это не похоже на работу, как описано, и я испытываю затруднения, решая, что именно не так.
Это ничем не отличается от обычной встроенной коллекции форм.
Однако, если вы хотите избежать проблем с настройками по умолчанию __NAME__
Прототип сталкивается со строкой прототипа родительской формы, вы должны взять взять, чтобы выбрать различные значения для TagType
а также SubTag
типы.
От запись Symfony Docs на CollectionType
:
prototype_name
- тип: строка по умолчанию: название
- Если у вас есть несколько коллекций в вашей форме, или хуже, Во вложенных коллекциях вы можете захотеть изменить местозаполнитель так, чтобы несвязанные заполнители не заменялись одинаковыми значениями.
Это может быть очень полезно, если вы хотите абстрагировать свои действия клонов с помощью JavaScript, как те, что в Эта статья (вставлено ниже), которое, кстати, похоже, нацелено на symfony3!
Например, вы можете захотеть включить то же значение, которое вы передаете prototype_name
в качестве атрибута в html держателя коллекции, чтобы вы могли получить к нему динамический доступ при выполнении replace
на data-prototype
HTML.
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);
}
Других решений пока нет …