You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为不同条目类型的集合生成两种幻灯片原型?

实现多类型幻灯片的表单原型生成方案

针对你的Doctrine JOINED继承场景,要生成图片/视频两种幻灯片的独立添加原型,可按以下步骤实现:

1. 拆分表单类型

为父类Slide和两个子类分别创建对应的表单类型,子类表单继承父表单的通用字段:

父表单:SlideType(处理通用属性)

// src/Form/Type/SlideType.php
namespace App\Form\Type;

use App\Entity\Slide;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class SlideType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('position')
            ->add('isPublished')
            // 其他通用属性,如标题、描述等
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Slide::class,
            'inherit_data' => true,
        ]);
    }
}

图片幻灯片表单:ImageSlideType

// src/Form/Type/ImageSlideType.php
namespace App\Form\Type;

use App\Entity\Image;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;

class ImageSlideType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('imageFile', FileType::class, [
                'required' => false,
                'label' => '图片文件'
            ])
            ->add('altText')
            // 添加隐藏字段标记类型,用于提交识别
            ->add('slide_type', HiddenType::class, [
                'data' => 'image',
                'mapped' => false
            ]);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Image::class,
        ]);
    }

    public function getParent(): string
    {
        return SlideType::class;
    }
}

视频幻灯片表单:VideoSlideType

// src/Form/Type/VideoSlideType.php
namespace App\Form\Type;

use App\Entity\Video;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;

class VideoSlideType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('videoUrl', UrlType::class, [
                'label' => '视频链接'
            ])
            ->add('autoplay')
            // 添加隐藏字段标记类型
            ->add('slide_type', HiddenType::class, [
                'data' => 'video',
                'mapped' => false
            ]);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Video::class,
        ]);
    }

    public function getParent(): string
    {
        return SlideType::class;
    }
}

2. 在滑块表单中生成双原型

在包含幻灯片集合的SliderType中,禁用默认原型,手动生成两种类型的原型并传递到视图:

// src/Form/Type/SliderType.php
namespace App\Form\Type;

use App\Entity\Slider;
use App\Entity\Image;
use App\Entity\Video;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

class SliderType extends AbstractType
{
    public function __construct(
        #[Autowire] private FormFactoryInterface $formFactory
    ) {}

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name')
            ->add('slides', CollectionType::class, [
                'entry_type' => SlideType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
                'prototype' => false, // 禁用默认原型
            ]);

        // 生成双原型并传递到视图
        $builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {
            $form = $event->getForm();

            // 创建图片幻灯片原型
            $imagePrototype = $this->formFactory->createNamed(
                'slides',
                ImageSlideType::class,
                new Image(),
                ['label' => false, 'attr' => ['class' => 'slide-prototype']]
            );

            // 创建视频幻灯片原型
            $videoPrototype = $this->formFactory->createNamed(
                'slides',
                VideoSlideType::class,
                new Video(),
                ['label' => false, 'attr' => ['class' => 'slide-prototype']]
            );

            // 将原型视图存入集合表单的变量中
            $form->get('slides')->setData([
                'image_prototype' => $imagePrototype->createView(),
                'video_prototype' => $videoPrototype->createView(),
            ]);
        });

        // 处理提交时的类型识别,动态匹配表单类型
        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
            $data = $event->getData();
            $form = $event->getForm();

            if (!isset($data['slides'])) return;

            foreach ($data['slides'] as $key => $slideData) {
                if ($slideData['slide_type'] === 'image') {
                    $form->get('slides')->add($key, ImageSlideType::class);
                } elseif ($slideData['slide_type'] === 'video') {
                    $form->get('slides')->add($key, VideoSlideType::class);
                }
            }
        });
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Slider::class,
        ]);
    }
}

3. 前端渲染双原型并实现添加逻辑

在Twig模板中,渲染两个原型并通过JS实现点击添加不同类型幻灯片的功能:

{# templates/slider/edit.html.twig #}
{{ form_start(form) }}
    {{ form_row(form.name) }}

    <h3>幻灯片列表</h3>
    <div id="slides-container" 
         data-image-prototype="{{ form_widget(form.slides.vars.image_prototype)|e('html_attr') }}"
         data-video-prototype="{{ form_widget(form.slides.vars.video_prototype)|e('html_attr') }}">
        {% for slide in form.slides %}
            {% if slide.vars.data is instance of('App\\Entity\\Image') %}
                <div class="slide-item image-slide">
                    {{ form_widget(slide) }}
                </div>
            {% elseif slide.vars.data is instance of('App\\Entity\\Video') %}
                <div class="slide-item video-slide">
                    {{ form_widget(slide) }}
                </div>
            {% endif %}
        {% endfor %}
    </div>

    <button type="button" id="add-image-btn">添加图片幻灯片</button>
    <button type="button" id="add-video-btn">添加视频幻灯片</button>

    <button type="submit">保存</button>
{{ form_end(form) }}

<script>
document.addEventListener('DOMContentLoaded', () => {
    const container = document.getElementById('slides-container');
    const addImageBtn = document.getElementById('add-image-btn');
    const addVideoBtn = document.getElementById('add-video-btn');
    let index = {{ form.slides|length }};

    addImageBtn.addEventListener('click', () => {
        const prototype = container.dataset.imagePrototype.replace(/__name__/g, index);
        container.insertAdjacentHTML('beforeend', prototype);
        index++;
    });

    addVideoBtn.addEventListener('click', () => {
        const prototype = container.dataset.videoPrototype.replace(/__name__/g, index);
        container.insertAdjacentHTML('beforeend', prototype);
        index++;
    });
});
</script>

关键说明

  • 禁用CollectionType的默认原型,手动生成子类型表单作为独立原型
  • 通过隐藏字段slide_type在提交时识别幻灯片类型,动态匹配对应的表单类型,确保Symfony能正确映射到Doctrine继承实体
  • 前端通过替换原型中的__name__占位符实现动态添加字段

内容的提问来源于stack exchange,提问作者victorfau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 05:45:31