如何使用 symfony3.x 正确显示多个 CollectionType 字段
How to properly display multiple CollectionType fields using symfony3.x
您好,我正在尝试为使用多个不同集合的实体建模。我尝试了 https://github.com/ninsuo/symfony-collection 项目,它提供了广泛的有用选项。我见过的最接近的示例是带有集合集合的示例,其中一个实体具有多个相同子 EntityType 的集合。我正在尝试使用多个不同的子 EntityType 集合实现相同的行为。
我的实体面临的问题是,当我只在其中放入一个集合时,代码工作正常,但是当我添加另一个子实体的第二个集合并发送我的表单时,我的控制器代码结束了删除其他集合的元素。我将其缩小到视图,因此我问的是那个特定项目。
我目前正在使用 Symfony 3.x,并且已经能够按照列出的示例进行操作,直到只使用一个集合才能很好地工作,我能够添加、删除和更新。
我的控制器代码:
namespace SigavFileBundle\Form;
use Ivory\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use SigavFileBundle\Entity\BookingAccommodation;
use SigavFileBundle\Entity\BookingIncludes;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class BookingType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('flights', CollectionType::class,
array(
'entry_type' => FlightDataType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__flights-collection__',
'attr' => [
'class' => "flights-collection",
],
)
)
->add('accommodations', CollectionType::class,
array(
'entry_type' => BookingAccommodationType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__accomm-collection__',
'attr' => [
'class' => "accomm-collection",
],
)
)
->add('cars', CollectionType::class,
array(
'entry_type' => BookingCarType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__cars-collection__',
'attr' => [
'class' => "cars-collection",
],
)
)
->add('transfers', CollectionType::class,
array(
'entry_type' => BookingTransferType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__transfers-collection__',
'attr' => [
'class' => "transfers-collection",
],
)
)
->add('excursions', CollectionType::class,
array(
'entry_type' => BookingExcursionType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__exc-collection__',
'attr' => [
'class' => "exc-collection",
],
)
)
->add('includes', CollectionType::class,
array(
'entry_type' => BookingIncludesType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__inc-collection__',
'attr' => [
'class' => "inc-collection",
],
)
)
->add('customActivities', CollectionType::class,
array(
'entry_type' => BookingCustomActivityType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__act-collection__',
'attr' => [
'class' => "act-collection",
],
)
)
->add('guides', CollectionType::class,
array(
'entry_type' => BookingGuideType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__guides-collection__',
'attr' => [
'class' => "guides-collection",
],
)
)
->add('commentaries', CollectionType::class,
array(
'entry_type' => BookingCommentariesType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__comm-collection__',
'attr' => [
'class' => "comm-collection",
],
)
)
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'SigavFileBundle\Entity\Booking'
));
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'sigavfilebundle_booking';
}
}
如您所见,多个不同类型的集合。接下来,这是其中两个 FormType 的代码,BookingAccommodationType 和 BookingCarType:
预订住宿类型:
namespace SigavFileBundle\Form;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use SigavGeneralBundle\Controller\MealPlanController;
use SigavGeneralBundle\Entity\Hotel;
use SigavGeneralBundle\Entity\RoomType;
use SigavGeneralBundle\SigavGeneralBundle;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
class BookingAccommodationType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
...
// Attributes go here
...
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'SigavFileBundle\Entity\BookingAccommodation'
));
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'sigavfilebundle_bookingaccommodation';
}
}
预订车型:
namespace SigavFileBundle\Form;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TimeType;
class BookingCarType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
...
// Attributes go here
...
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'SigavFileBundle\Entity\BookingCar'
));
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'sigavfilebundle_bookingcar';
}
}
所有与视图相关的代码如下。
main_view.html.twig:
<!-- A lot of HTML code before //-->
{%
form_theme form.accommodations
'jquery.collection.html.twig'
'booking/bookingAccommodations.html.twig'
%}
{{ form( form.accommodations) }}
<!-- A lot of HTML code between //-->
{%
form_theme form.cars
'jquery.collection.html.twig'
'booking/bookingCars.html.twig'
%}
{{ form( form.cars) }}
<!-- A lot of HTML code after //-->
<script>
function initCollectionHolders( $id, $form_id )
{
$($id).collection({
name_prefix: $form_id,
add_at_the_end: true,
allow_add: 1,
allow_duplicate: 1,
allow_remove: 1,
duplicate: '<a href="#"><span class="pe-7s-copy"></span></a>',
add: '<a href="#"><span class="pe-7s-plus"></span></a>',
remove: '<a href="#"><span class="pe-7s-close-circle"></span></a>'
});
}
initCollectionHolders('.accomm-collection', '{{ form.accommodations.vars.full_name }}');
initCollectionHolders('.cars-collection', '{{ form.cars.vars.full_name }}');
<script>
bookingCars.html.twig:
{% block sigavfilebundle_bookingcar_label %}{% endblock %}
{% block sigavfilebundle_bookingcar_widget %}
{# HERE GOES THE ENTIRE FORM LAYOUT #}
{% endblock %}
bookingAccommodations.html.twig:
{% block sigavfilebundle_bookingaccommodation_label %}{% endblock %}
{% block sigavfilebundle_bookingaccommodation_widget %}
{# HERE GOES THE ENTIRE FORM LAYOUT #}
{% endblock %}
jquery.collection.html.twig:
{% block collection_widget %}
{% spaceless %}
{% if prototype is defined %}
{% set attr = attr|merge({'data-prototype': form_row(prototype)}) %}
{% set attr = attr|merge({'data-prototype-name': prototype.vars.name}) %}
{% endif %}
{% set attr = attr|merge({'data-allow-add': allow_add ? 1 : 0}) %}
{% set attr = attr|merge({'data-allow-remove': allow_delete ? 1 : 0 }) %}
{% set attr = attr|merge({'data-name-prefix': full_name}) %}
{{ block('form_widget') }}
{% endspaceless %}
{% endblock collection_widget %}
我首先想知道 symfony-collection 库是否可以在具有一个实体和多个不同子类型集合的环境中使用
提前致谢...
name_prefix
选项仅用于嵌套的 collections,它将有助于使用原型生成新条目,而不会在两个或多个 collections 之间发生冲突。
您正在寻找的是 prefix
选项,用于为 symfony-collection 插件生成的所有选择器添加前缀。在您的情况下,所有 collection 的前缀都相同,因此单击一个或另一个按钮将触发对相同 collection.
的操作
如果您希望在同一页面上创建多个 collection,您需要更改 collection 前缀,以便插件为正确的 collection.
例如:
$('.collectionA').collection({
'prefix': 'first-collection'
});
$('.collectionB').collection({
'prefix': 'second-collection'
});
然后如果你想编辑那些 collections 表单主题,你需要用 first-collection-add 替换 collection-add 在你的添加按钮上 for exmaple。
<a href="#" class="first-collection-add btn btn-default">
<span class="glyphicon glyphicon-plus-sign"></span>
</a>
有关 运行 示例,请参阅 this demo。
您好,我正在尝试为使用多个不同集合的实体建模。我尝试了 https://github.com/ninsuo/symfony-collection 项目,它提供了广泛的有用选项。我见过的最接近的示例是带有集合集合的示例,其中一个实体具有多个相同子 EntityType 的集合。我正在尝试使用多个不同的子 EntityType 集合实现相同的行为。
我的实体面临的问题是,当我只在其中放入一个集合时,代码工作正常,但是当我添加另一个子实体的第二个集合并发送我的表单时,我的控制器代码结束了删除其他集合的元素。我将其缩小到视图,因此我问的是那个特定项目。
我目前正在使用 Symfony 3.x,并且已经能够按照列出的示例进行操作,直到只使用一个集合才能很好地工作,我能够添加、删除和更新。
我的控制器代码:
namespace SigavFileBundle\Form;
use Ivory\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use SigavFileBundle\Entity\BookingAccommodation;
use SigavFileBundle\Entity\BookingIncludes;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class BookingType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('flights', CollectionType::class,
array(
'entry_type' => FlightDataType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__flights-collection__',
'attr' => [
'class' => "flights-collection",
],
)
)
->add('accommodations', CollectionType::class,
array(
'entry_type' => BookingAccommodationType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__accomm-collection__',
'attr' => [
'class' => "accomm-collection",
],
)
)
->add('cars', CollectionType::class,
array(
'entry_type' => BookingCarType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__cars-collection__',
'attr' => [
'class' => "cars-collection",
],
)
)
->add('transfers', CollectionType::class,
array(
'entry_type' => BookingTransferType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__transfers-collection__',
'attr' => [
'class' => "transfers-collection",
],
)
)
->add('excursions', CollectionType::class,
array(
'entry_type' => BookingExcursionType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__exc-collection__',
'attr' => [
'class' => "exc-collection",
],
)
)
->add('includes', CollectionType::class,
array(
'entry_type' => BookingIncludesType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__inc-collection__',
'attr' => [
'class' => "inc-collection",
],
)
)
->add('customActivities', CollectionType::class,
array(
'entry_type' => BookingCustomActivityType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__act-collection__',
'attr' => [
'class' => "act-collection",
],
)
)
->add('guides', CollectionType::class,
array(
'entry_type' => BookingGuideType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__guides-collection__',
'attr' => [
'class' => "guides-collection",
],
)
)
->add('commentaries', CollectionType::class,
array(
'entry_type' => BookingCommentariesType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'prototype_name' => '__comm-collection__',
'attr' => [
'class' => "comm-collection",
],
)
)
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'SigavFileBundle\Entity\Booking'
));
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'sigavfilebundle_booking';
}
}
如您所见,多个不同类型的集合。接下来,这是其中两个 FormType 的代码,BookingAccommodationType 和 BookingCarType:
预订住宿类型:
namespace SigavFileBundle\Form;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use SigavGeneralBundle\Controller\MealPlanController;
use SigavGeneralBundle\Entity\Hotel;
use SigavGeneralBundle\Entity\RoomType;
use SigavGeneralBundle\SigavGeneralBundle;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
class BookingAccommodationType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
...
// Attributes go here
...
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'SigavFileBundle\Entity\BookingAccommodation'
));
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'sigavfilebundle_bookingaccommodation';
}
}
预订车型:
namespace SigavFileBundle\Form;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TimeType;
class BookingCarType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
...
// Attributes go here
...
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'SigavFileBundle\Entity\BookingCar'
));
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'sigavfilebundle_bookingcar';
}
}
所有与视图相关的代码如下。
main_view.html.twig:
<!-- A lot of HTML code before //-->
{%
form_theme form.accommodations
'jquery.collection.html.twig'
'booking/bookingAccommodations.html.twig'
%}
{{ form( form.accommodations) }}
<!-- A lot of HTML code between //-->
{%
form_theme form.cars
'jquery.collection.html.twig'
'booking/bookingCars.html.twig'
%}
{{ form( form.cars) }}
<!-- A lot of HTML code after //-->
<script>
function initCollectionHolders( $id, $form_id )
{
$($id).collection({
name_prefix: $form_id,
add_at_the_end: true,
allow_add: 1,
allow_duplicate: 1,
allow_remove: 1,
duplicate: '<a href="#"><span class="pe-7s-copy"></span></a>',
add: '<a href="#"><span class="pe-7s-plus"></span></a>',
remove: '<a href="#"><span class="pe-7s-close-circle"></span></a>'
});
}
initCollectionHolders('.accomm-collection', '{{ form.accommodations.vars.full_name }}');
initCollectionHolders('.cars-collection', '{{ form.cars.vars.full_name }}');
<script>
bookingCars.html.twig:
{% block sigavfilebundle_bookingcar_label %}{% endblock %}
{% block sigavfilebundle_bookingcar_widget %}
{# HERE GOES THE ENTIRE FORM LAYOUT #}
{% endblock %}
bookingAccommodations.html.twig:
{% block sigavfilebundle_bookingaccommodation_label %}{% endblock %}
{% block sigavfilebundle_bookingaccommodation_widget %}
{# HERE GOES THE ENTIRE FORM LAYOUT #}
{% endblock %}
jquery.collection.html.twig:
{% block collection_widget %}
{% spaceless %}
{% if prototype is defined %}
{% set attr = attr|merge({'data-prototype': form_row(prototype)}) %}
{% set attr = attr|merge({'data-prototype-name': prototype.vars.name}) %}
{% endif %}
{% set attr = attr|merge({'data-allow-add': allow_add ? 1 : 0}) %}
{% set attr = attr|merge({'data-allow-remove': allow_delete ? 1 : 0 }) %}
{% set attr = attr|merge({'data-name-prefix': full_name}) %}
{{ block('form_widget') }}
{% endspaceless %}
{% endblock collection_widget %}
我首先想知道 symfony-collection 库是否可以在具有一个实体和多个不同子类型集合的环境中使用
提前致谢...
name_prefix
选项仅用于嵌套的 collections,它将有助于使用原型生成新条目,而不会在两个或多个 collections 之间发生冲突。
您正在寻找的是 prefix
选项,用于为 symfony-collection 插件生成的所有选择器添加前缀。在您的情况下,所有 collection 的前缀都相同,因此单击一个或另一个按钮将触发对相同 collection.
如果您希望在同一页面上创建多个 collection,您需要更改 collection 前缀,以便插件为正确的 collection.
例如:
$('.collectionA').collection({
'prefix': 'first-collection'
});
$('.collectionB').collection({
'prefix': 'second-collection'
});
然后如果你想编辑那些 collections 表单主题,你需要用 first-collection-add 替换 collection-add 在你的添加按钮上 for exmaple。
<a href="#" class="first-collection-add btn btn-default">
<span class="glyphicon glyphicon-plus-sign"></span>
</a>
有关 运行 示例,请参阅 this demo。