带有子集合的 Symfony 3 表单集合
Symfony 3 form collection with sub-collection
我有一个表单,其中包含一个名为 "planes" 的字段。
该字段可以是多个(集合),具有以下子字段:
- 速度
- 重量
- 颜色
- 引擎
子字段"engines"也是字段的集合:
- 转速
- fuel_type
- 体重
我想知道 Symfony 表单生成器会是什么样子,因为用户应该可以根据需要添加尽可能多的平面。这同样适用于引擎。
看起来像这样:
class 的建造者有飞机 collection:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('planes', CollectionType::class, array(
'entry_type' => PlaneType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,// this will call get/set of your entity
'prototype' => true, // is needed coz there will be 2 prototypes
'prototype_name'=> '__planes__'//1-st prototype name
'attr' => [
'class' => 'embeddedCollection'
]
));
}
PlaneType 中的建造者 class:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('speed');
$builder->add('weight');
$builder->add('color');
$builder->add('engines', CollectionType::class, array(
'entry_type' => EngineType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,// this will call get/set of your entity
'prototype' => true, // is needed coz there will be 2 prototypes
'prototype_name'=> '__engines__',//2 -nd prototype
'attr' => [
'class' => 'subEmbeddedCollection'
]
));
}
EngineType 中的构建器 class:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('rpm');
$builder->add('fuel_type');
$builder->add('weight');
}
您还应该为动态 adding/removing 字段添加一些 javascript,在添加过程中您应该将原型名称与字段编号的 id 交换。
编辑:
一些带有 JQuery 的 js 代码使其在 front-end:
上工作
function initCollection(collectionHolder, addItemButton, deleteItemButton, prototypePattern) {
let $addItemButton = $(addItemButton);
// Get the ul that holds the collection of tags
let $collectionHolder = $(collectionHolder);
// add a delete link to all of the existing tag form li elements
$collectionHolder.children('.form-group').each(function () {
addFormDeleteLink($(this), deleteItemButton);
});
// add the "add a tag" anchor and li to the tags ul
$collectionHolder.append($addItemButton);
// 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);
//when new element is added then trigger event on collection
let addNewElementEvent = jQuery.Event ('element-added');
let addNewChildCollection = jQuery.Event ('new-child-collection');
$addItemButton.on('click', function (e) {
// add a new tag form (see next code block)
lastAddedElement = addForm($collectionHolder, $addItemButton, deleteItemButton, prototypePattern);
if($collectionHolder.hasClass('embeddedCollection')) {
$collectionHolder.trigger(addNewElementEvent);
}else{
$collectionHolder.trigger(addNewChildCollection);
}
});
}
function addForm($collectionHolder, $addItemButton, deleteItemButton, prototypePattern) {
// Get the data-prototype explained earlier
let prototype = $collectionHolder.data('prototype');
// get the new index
let index = $collectionHolder.data('index');
let newForm = prototype;
newForm = newForm.replace(prototypePattern, 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
let $newFormLi = $(newForm);
$addItemButton.before($newFormLi);
addFormDeleteLink($newFormLi, deleteItemButton);
return $newFormLi;
}
function addFormDeleteLink($tagFormLi, deleteItemButton) {
let $removeFormButton = $(deleteItemButton);
$tagFormLi.append($removeFormButton);
$removeFormButton.on('click', function (e) {
// remove the li for the tag form
let $parent = $tagFormLi.parent();
$tagFormLi.remove();
if($removeFormButton.hasClass('labels-rewrite'))
{
$parent.trigger(jQuery.Event('labels-rewrite'));
}
});
}
parent collection 应该有 class embeddedCollection
,然后如果添加了新的 child 将触发事件 'element-added'
.如果添加了带有 child collection 引擎的新飞机,您需要在添加的引擎 collection.
上调用 initCollection 方法
所以首先你需要初始化现有的 collections,例如:
//init all sub collections of engines of the planes
$('.subEmbeddedCollection').each(function () {
initCollection($(this),
'<button type="button">Add engine</button>',
'<button type="button" >Delete engine</button>',
/__engines__/g
);
});
//init main collection of planes
let $collectionHolder = $('.embeddedCollection');
initCollection($collectionHolder,
'<button type="button">Add plane</button>',
'<button type="button" >Delete plane</button>',
/__planes__/g
);
然后当事件 element-added 在主要 collection 平面中被触发时,您应该初始化所有动态添加的引擎子集合:
//init collection in added plane
$collectionHolder.on('element-added', function () {
let $lastChildCollection = $(this).find('.engines').last();
initCollection($lastChildCollection,
'<button type="button">Add engine</button>',
'<button type="button" >Delete engine</button>',
/__exam_type_lab_request__/g
);
});
我有一个表单,其中包含一个名为 "planes" 的字段。
该字段可以是多个(集合),具有以下子字段: - 速度 - 重量 - 颜色 - 引擎
子字段"engines"也是字段的集合: - 转速 - fuel_type - 体重
我想知道 Symfony 表单生成器会是什么样子,因为用户应该可以根据需要添加尽可能多的平面。这同样适用于引擎。
看起来像这样:
class 的建造者有飞机 collection:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('planes', CollectionType::class, array(
'entry_type' => PlaneType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,// this will call get/set of your entity
'prototype' => true, // is needed coz there will be 2 prototypes
'prototype_name'=> '__planes__'//1-st prototype name
'attr' => [
'class' => 'embeddedCollection'
]
));
}
PlaneType 中的建造者 class:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('speed');
$builder->add('weight');
$builder->add('color');
$builder->add('engines', CollectionType::class, array(
'entry_type' => EngineType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,// this will call get/set of your entity
'prototype' => true, // is needed coz there will be 2 prototypes
'prototype_name'=> '__engines__',//2 -nd prototype
'attr' => [
'class' => 'subEmbeddedCollection'
]
));
}
EngineType 中的构建器 class:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('rpm');
$builder->add('fuel_type');
$builder->add('weight');
}
您还应该为动态 adding/removing 字段添加一些 javascript,在添加过程中您应该将原型名称与字段编号的 id 交换。
编辑: 一些带有 JQuery 的 js 代码使其在 front-end:
上工作function initCollection(collectionHolder, addItemButton, deleteItemButton, prototypePattern) {
let $addItemButton = $(addItemButton);
// Get the ul that holds the collection of tags
let $collectionHolder = $(collectionHolder);
// add a delete link to all of the existing tag form li elements
$collectionHolder.children('.form-group').each(function () {
addFormDeleteLink($(this), deleteItemButton);
});
// add the "add a tag" anchor and li to the tags ul
$collectionHolder.append($addItemButton);
// 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);
//when new element is added then trigger event on collection
let addNewElementEvent = jQuery.Event ('element-added');
let addNewChildCollection = jQuery.Event ('new-child-collection');
$addItemButton.on('click', function (e) {
// add a new tag form (see next code block)
lastAddedElement = addForm($collectionHolder, $addItemButton, deleteItemButton, prototypePattern);
if($collectionHolder.hasClass('embeddedCollection')) {
$collectionHolder.trigger(addNewElementEvent);
}else{
$collectionHolder.trigger(addNewChildCollection);
}
});
}
function addForm($collectionHolder, $addItemButton, deleteItemButton, prototypePattern) {
// Get the data-prototype explained earlier
let prototype = $collectionHolder.data('prototype');
// get the new index
let index = $collectionHolder.data('index');
let newForm = prototype;
newForm = newForm.replace(prototypePattern, 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
let $newFormLi = $(newForm);
$addItemButton.before($newFormLi);
addFormDeleteLink($newFormLi, deleteItemButton);
return $newFormLi;
}
function addFormDeleteLink($tagFormLi, deleteItemButton) {
let $removeFormButton = $(deleteItemButton);
$tagFormLi.append($removeFormButton);
$removeFormButton.on('click', function (e) {
// remove the li for the tag form
let $parent = $tagFormLi.parent();
$tagFormLi.remove();
if($removeFormButton.hasClass('labels-rewrite'))
{
$parent.trigger(jQuery.Event('labels-rewrite'));
}
});
}
parent collection 应该有 class embeddedCollection
,然后如果添加了新的 child 将触发事件 'element-added'
.如果添加了带有 child collection 引擎的新飞机,您需要在添加的引擎 collection.
所以首先你需要初始化现有的 collections,例如:
//init all sub collections of engines of the planes
$('.subEmbeddedCollection').each(function () {
initCollection($(this),
'<button type="button">Add engine</button>',
'<button type="button" >Delete engine</button>',
/__engines__/g
);
});
//init main collection of planes
let $collectionHolder = $('.embeddedCollection');
initCollection($collectionHolder,
'<button type="button">Add plane</button>',
'<button type="button" >Delete plane</button>',
/__planes__/g
);
然后当事件 element-added 在主要 collection 平面中被触发时,您应该初始化所有动态添加的引擎子集合:
//init collection in added plane
$collectionHolder.on('element-added', function () {
let $lastChildCollection = $(this).find('.engines').last();
initCollection($lastChildCollection,
'<button type="button">Add engine</button>',
'<button type="button" >Delete engine</button>',
/__exam_type_lab_request__/g
);
});