Symfony 6:嵌入表单集合仅添加新元素,忽略已有元素
问题:Symfony嵌入表单集合仅允许添加新附件,禁止编辑已有附件
我有一个包含嵌入集合的FormType,完全遵循Symfony官方《嵌入表单集合》文档实现。嵌入的AttachmentType包含标题和文件上传字段,代码如下:
// AttachmentType.php $builder ->add('title', null, ['label' => 'Title', 'required' => true]) ->add('attachmentFile', FileType::class, [ 'label' => 'File', 'mapped' => false, 'required' => true, 'constraints' => [ new File([ 'maxSize' => '1024k', ]) ], ]);
编辑父对象时,希望仅允许添加新附件,不允许编辑已有附件(避免每次编辑都要重新上传旧文件)。现有Stimulus控制器和Twig模板按文档实现,但找不到CollectionType相关配置,且不想修改现有JS组件,求实现方案。
现有JS控制器代码:
// assets/controllers/form-collection_controller.js import { Controller } from '@hotwired/stimulus'; export default class extends Controller { static targets = ["collectionContainer"] static values = { index : Number, prototype: String, } addCollectionElement(event) { const item = document.createElement('li'); item.innerHTML = this.prototypeValue.replace(/__name__/g, this.indexValue); this.collectionContainerTarget.appendChild(item); this.indexValue++; } }
现有Twig模板代码:
<div {{ stimulus_controller('form-collection') }} data-form-collection-index-value="{{ form.attachments|length > 0 ? form.attachments|last.vars.name + 1 : 0 }}" data-form-collection-prototype-value="{{ form_widget(form.attachments.vars.prototype)|e('html_attr') }}" > <ul {{ stimulus_target('form-collection', 'collectionContainer') }}></ul> <button type="button" class="btn btn-secondary" {{ stimulus_action('form-collection', 'addCollectionElement') }}>Añadir adjunto</button> </div>
解决方案
1. 表单配置:让CollectionType只处理新提交的附件
在父FormType的attachments字段配置里,设置allow_add为true,同时不自动映射已有附件,避免表单处理时干扰旧数据,只保留新增附件的原型功能:
// ParentFormType.php $builder->add('attachments', CollectionType::class, [ 'entry_type' => AttachmentType::class, 'allow_add' => true, 'mapped' => false, // 关键:不对已有附件实体生成表单输入 'prototype' => true, 'entry_options' => ['label' => false], ]);
2. Twig模板:仅保留新增附件入口
保持collectionContainer为空(不渲染已有附件的编辑字段),调整索引值计算逻辑,确保新增字段的索引从已有附件数量开始,避免冲突:
<div {{ stimulus_controller('form-collection') }} data-form-collection-index-value="{{ form.attachments|length }}" data-form-collection-prototype-value="{{ form_widget(form.attachments.vars.prototype)|e('html_attr') }}" > <ul {{ stimulus_target('form-collection', 'collectionContainer') }}></ul> <button type="button" class="btn btn-secondary" {{ stimulus_action('form-collection', 'addCollectionElement') }}>添加附件</button> </div>
3. 控制器处理:手动保存新附件
在父对象的编辑控制器中,手动提取表单提交的新附件数据,创建Attachment实体并关联到父对象:
// ParentController.php public function edit(Request $request, ParentEntity $parent): Response { $form = $this->createForm(ParentFormType::class, $parent); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { // 获取提交的新附件数据 $attachmentsData = $form->get('attachments')->getData(); foreach ($attachmentsData as $attachmentData) { $attachment = new Attachment(); $attachment->setTitle($attachmentData['title']); // 处理文件上传(示例逻辑,可根据实际存储方式调整) $file = $attachmentData['attachmentFile']; $fileName = md5(uniqid()) . '.' . $file->guessExtension(); $file->move( $this->getParameter('attachments_directory'), $fileName ); $attachment->setAttachmentFileName($fileName); $parent->addAttachment($attachment); } $this->getDoctrine()->getManager()->flush(); return $this->redirectToRoute('parent_index'); } return $this->render('parent/edit.html.twig', [ 'parent' => $parent, 'form' => $form->createView(), ]); }
4. 可选:页面展示已有附件(非编辑)
如果需要在编辑页面展示已有附件,直接从父对象的关联中读取渲染即可,无需做成表单字段:
{# 编辑页面合适位置添加 #} <h3>已有附件</h3> <ul> {% for attachment in parent.attachments %} <li> {{ attachment.title }} - <a href="{{ asset('uploads/attachments/' ~ attachment.attachmentFileName) }}">下载</a> </li> {% endfor %} </ul>
通过以上步骤,即可实现编辑父对象时仅允许添加新附件、不干扰已有附件的需求,且无需修改现有Stimulus控制器。
内容的提问来源于stack exchange,提问作者K. Weber
相关产品推荐
相关产品推荐

