如何嵌入一组形式

编辑该页面

警告:你浏览的文档欧宝体育电话欧宝娱乐app下载地址Symfony 5.2,不再维护。

这个页面的更新版本Symf欧宝娱乐app下载地址ony 6.2(当前的稳定版本)。

任务类,在相同的形式,你就可以编辑、创建和删除标签对象相关的任务。

我们首先创建一个任务实体:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31
/ / src /实体/ Task.php名称空间应用程序\实体;使用学说\常见的\集合\ArrayCollection;使用学说\常见的\集合\集合;任务{受保护的美元描述;受保护的美元标签;公共函数__construct(){美元- >标签=ArrayCollection ();}公共函数getDescription():字符串{返回美元- >描述;}公共函数setDescription(字符串美元描述):无效{美元- >描述=美元描述;}公共函数getTags():集合{返回美元- >标签;}}

请注意

ArrayCollection是特定于教条和类似于PHP数组,但提供了许多实用方法。

现在,创建一个标签类。正如您在上面看到的,任务可以有多个标签对象:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/ / src /实体/ Tag.php名称空间应用程序\实体;标签{私人美元的名字;公共函数getName():字符串{返回美元- >名称;}公共函数setName(字符串美元的名字):无效{美元- >name =美元的名字;}}

然后,创建一个类,这样一个形式标签对象由用户可以修改:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/ / src /形式/ TagType.php名称空间应用程序\形式;使用应用程序\实体\标签;使用欧宝娱乐app下载地址\组件\形式\AbstractType;使用欧宝娱乐app下载地址\组件\形式\FormBuilderInterface;使用欧宝娱乐app下载地址\组件\OptionsResolver\OptionsResolver;TagType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项):无效{美元构建器- >add (“名字”);}公共函数configureOptions(OptionsResolver美元解析器):无效{美元解析器- >setDefaults ([“data_class”= >标记::类,]);}}

接下来,让我们创建一个表单任务实体,使用CollectionType领域的TagType形式。这将允许我们修改的所有标签的元素任务对在任务表单:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27日28
/ / src /形式/ TaskType.php名称空间应用程序\形式;使用应用程序\实体\任务;使用欧宝娱乐app下载地址\组件\形式\AbstractType;使用欧宝娱乐app下载地址\组件\形式\扩展\核心\类型\CollectionType;使用欧宝娱乐app下载地址\组件\形式\FormBuilderInterface;使用欧宝娱乐app下载地址\组件\OptionsResolver\OptionsResolver;TaskType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项):无效{美元构建器- >add (“描述”);美元构建器- >add (“标签”,CollectionType::类,“entry_type”= > TagType::类,“entry_options”= > [“标签”= >)));}公共函数configureOptions(OptionsResolver美元解析器):无效{美元解析器- >setDefaults ([“data_class”= >任务::类,]);}}

在控制器中,您将创建一个新形式的TaskType:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34 35 36 37 38 39
/ / src /控制器/ TaskController.php名称空间应用程序\控制器;使用应用程序\实体\标签;使用应用程序\实体\任务;使用应用程序\形式\TaskType;使用欧宝娱乐app下载地址\\FrameworkBundle\控制器\AbstractController;使用欧宝娱乐app下载地址\组件\HttpFoundation\请求;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;TaskController扩展AbstractController{公共函数(请求美元请求):响应{美元任务=任务();/ /伪代码-一些示例标记添加到任务/ /(否则,模板将呈现一个空列表的标签)美元标签1=标签();美元标签1- >setName (“标签1”);美元任务- >getTags ()- >add (美元标签1);美元标签2=标签();美元标签2- >setName (标签2的);美元任务- >getTags ()- >add (美元标签2);/ /结束伪代码美元形式=美元- >createForm (TaskType::类,美元任务);美元形式- >handleRequest (美元请求);如果(美元形式- >isSubmitted () & &美元形式- >isValid ()) {/ /……表单处理、喜欢拯救任务和标签实体吗}返回美元- >呈现(“任务/ new.html.twig”,(“形式”= >美元形式- >createView ()));}}

模板中,您现在可以遍历的存在TagType形式呈现:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
{/任务/ new.html #模板。树枝#}{#……#}{{form_start(形式)}}{{form_row (form.description)}}<h3>标签< /h3><ul=“标签”>{%标签的形式。标签%}<>{{form_row (tag.name)}}< />{%endfor%}< /ul>{{form_end(形式)}}{#……#}

当用户提交表单时,提交的数据标签字段是用来构造一个ArrayCollection标签对象。然后设置集合标签场的任务并可以通过访问$任务- > getTags ()

到目前为止,这工作不错,但只有编辑现有的标签。它不允许我们添加新标签或删除现有的。

谨慎

你可以嵌入嵌套你喜欢收集尽可能多的水平下降。然而,如果您使用Xdebug,你可以收到“100”达到的最大函数嵌套级,流产!错误。为了解决这个问题,增加了xdebug.max_nesting_levelPHP设置,使用手工或渲染每个表单字段form_row ()而不是渲染整个形式(如form_widget(形式))。

allow_add选择:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
/ / src /形式/ TaskType.php/ /……公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项):无效{/ /……美元构建器- >add (“标签”,CollectionType::类,“entry_type”= > TagType::类,“entry_options”= > [“标签”= >),“allow_add”= >真正的]);}

allow_add选择也让原型变量可用。这个“原型”有点“模板”,其中包含所需的所有HTML JavaScript动态创建任何新的“标签”形式。呈现的原型,添加以下data-prototype属性到现有的< ul >在你的模板:

1
<ul=“标签”data-prototype={{form_widget (form.tags.vars.prototype) | e (html_attr)}}>< /ul>

现在添加一个按钮的旁边< ul >动态添加一个新的标签:

1
<按钮类型=“按钮”=“add_item_link”data-collection-holder-class=“标签”>添加一个标签< /按钮>

在呈现页面,结果是这样的:

1
<ul=“标签”材料指数=”{{形式。标签|length > 0 ? form.tags|last.vars.name + 1 : 0 }}"< /span>data-prototype=& lt;div>< /span>& lt;标签类="< /span>要求"< /span>>< /span>__name__& lt;/标签>< /span>& lt;div id ="< /span>task_tags___name__"< /span>>< /span>& lt;div>< /span>& lt;标签="< /span>task_tags___name___name"< /span>类="< /span>要求"< /span>>< /span>的名字& lt;/标签>< /span>& lt;输入类型="< /span>文本"< /span>id ="< /span>task_tags___name___name"< /span>name ="< /span>任务[标记][__name__][名称]"< /span>要求="< /span>要求"< /span>最大长度="< /span>255年"< /span>/>< /span>& lt;/ div>< /span>& lt;/ div>< /span>& lt;/ div>< /span>”>

提示

form.tags.vars.prototype是一个表单元素,外观和感觉就像个人吗form_widget(标签)元素在你循环。这意味着您可以调用form_widget (),form_row ()了form_label ()在上面。你甚至可以选择只显示字段(例如的名字字段):

1
{{form_widget (form.tags.vars.prototype.name) | e}}

请注意

如果你使你的整个“标签”sub-form(如。form_row (form.tags)),data-prototype属性是自动添加到包含div,你需要相应地调整以下JavaScript。

现在添加一些JavaScript来读这个属性和动态添加新的标签形式当用户单击“添加一个标签”链接。这个示例使用jQuery并假设您已包括在您的页面(如使用Symfony的欧宝娱乐app下载地址Webpack安可)。

添加一个<脚本>标签页面上的某个地方,包括所需的JavaScript功能:

1 2 3
文档.querySelectorAll (“.add_item_link”).forEach (btn= >btn.addEventListener (“点击”addFormToCollection));

addFormToCollection ()函数的工作将被使用data-prototype动态属性来添加一个新的形式当单击这个链接。的data-prototypeHTML包含标签的文本输入元素的名称任务[标记][__name__][名称]和身份证task_tags___name___name。的__name__是一个占位符,你会替换为一个独特的,递增(如数量。任务[标记][3][名称]):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
常量addFormToCollection =(e)= >{常量collectionHolder =文档.querySelector (“。”+ e.currentTarget.dataset.collectionHolderClass);常量项=文档.createElement (“李”);项。innerHTML = collectionHolder .dataset .prototype .replace (/ __name__ / g,collectionHolder.dataset。指数);collectionHolder.appendChild(项);collectionHolder.dataset.index + +;};

现在,每当用户单击添加一个标签链接时,会出现一个新的接头形式在页面上。当提交表单时,任何新的标签将被转换成新的形式标签对象和添加到标签财产的任务对象。

另请参阅

你可以找到一个工作示例JSFiddle

简化处理这些新标签,添加一个“加法器”和“剂”的标记方法任务类:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/ / src /实体/ Task.php名称空间应用程序\实体;/ /……任务{/ /……公共函数addTag(标签美元标签):无效{美元- >标签- >add (美元标签);}公共函数removeTag(标签美元标签):无效{/ /……}}

接下来,添加一个by_reference可以选择的标签场和设置它:

1 2 3 4 5 6 7 8 9 10 11 12
/ / src /形式/ TaskType.php/ /……公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项):无效{/ /……美元构建器- >add (“标签”,CollectionType::类,/ /……“by_reference”= >]);}

通过这两个变化,当提交表单时,每一个新的标签对象添加到任务通过调用类addTag ()方法。这种变化之前,他们由形式通过调用内部补充道$任务- > getTags() - >添加标签($)。那是很好,但强迫使用方法使处理这些新的“毒蛇”标签对象更容易(尤其是如果你使用原则,您将了解下一个!)。

谨慎

你必须创建这两个addTag ()removeTag ()方法,否则形式将仍在使用setTag ()即使by_reference。你会了解更多的removeTag ()方法在本文的后面。

谨慎

欧宝娱乐app下载地址Symfony只能使plural-to-singular转换(例如标签财产addTag ()英语单词的方法)。任何其他语言编写的代码无法正常工作。

拯救教义的新标签,您需要考虑一些事情。首先,除非你遍历所有的新标签对象和调用entityManager - >保存美元(美元)在每一个,你会收到一个错误从学说:

发现一个新实体的关系应用实体\ \任务#标签这不是配置为级联保存操作实体……

为了解决这个问题,您可以选择“级联”持续的自动操作任务反对任何相关标签。为此,添加级联选择你的元数据:

  • 注释
  • YAML
  • XML
1 2 3 4 5 6 7 8
/ / src /实体/ Task.php/ /……/ * * *@ORM\多(targetEntity =“App \实体\标签”,级联={“持久化”})* /受保护的美元标签;

第二个潜在的问题处理拥有和逆学说的关系。在这个例子中,如果“拥有”的关系是“任务”,然后坚持会工作好标签是正确添加到任务。然而,如果拥有一方在“标签”,那么你就需要做更多的工作以确保修改正确的关系。

关键是确保一个“任务”设置在每一个“标签”。一种方法是添加一些额外的逻辑addTag (),叫做表单类型by_reference被设置为:

1 2 3 4 5 6 7 8 9 10 11 12 13
/ / src /实体/ Task.php/ /……公共函数addTag(标签美元标签):无效{/ /多对多关联:美元标签- >addTask (美元);/ /多对一关联:美元标签- >setTask (美元);美元- >标签- >add (美元标签);}

如果你要的addTask (),确保你有一个适当的方法是这样的:

1 2 3 4 5 6 7 8 9
/ / src /实体/ Tag.php/ /……公共函数addTask(任务美元任务):无效{如果(!美元- >任务- >包含(美元任务)){美元- >任务- >add (美元任务);}}

allow_delete选择的类型:

1 2 3 4 5 6 7 8 9 10 11 12
/ / src /形式/ TaskType.php/ /……公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项):无效{/ /……美元构建器- >add (“标签”,CollectionType::类,/ /……“allow_delete”= >真正的]);}

现在,你需要把一些代码removeTag ()的方法任务:

1 2 3 4 5 6 7 8 9 10 11 12
/ / src /实体/ Task.php/ /……任务{/ /……公共函数removeTag(标签美元标签):无效{美元- >标签- >removeElement (美元标签);}}

allow_delete集合的选择意味着如果一个项目没有发送提交,从集合中删除相关数据在服务器上。为了使这个工作在一个HTML表单,您必须删除集合的DOM元素条目被删除,在提交表单之前。

首先,添加一个“删除这个标签”链接到每个标记形式:

1 2 3 4 5 6 7 8 9 10 11 12 13
常量标签=文档.querySelectorAll (“ul.tags”)tags.forEach ((标签)= >{addTagFormDeleteLink(标签)})/ /……从上面剩下的块函数addFormToCollection(){/ /……/ /添加一个删除链接到新形式addTagFormDeleteLink(项);}

addTagFormDeleteLink ()函数将看起来像这样:

1 2 3 4 5 6 7 8 9 10 11 12 13
常量addTagFormDeleteLink =(tagFormLi)= >{常量removeFormButton =文档.createElement (“按钮”)removeFormButton。类List removeFormButton.innerText =删除这个标签的tagFormLi.append (removeFormButton);removeFormButton.addEventListener (“点击”,(e) = > {e.preventDefault ()/ /删除标记的李形式tagFormLi.remove ();});}

当删除一个标签的形式从DOM并提交,删除标签对象将不会被包括在收集传递给setTags ()。根据您的持久层,这实际上可能不足以消除了之间的关系标签任务对象。

以这种方式删除对象时,您可能需要做更多的工作以确保之间的关系任务和删除标签正确地删除。

在教义,有双方的关系:拥有方和反的一面。通常在这种情况下,你就会拥有一个多对一的关系,删除标签将会消失,坚持正确(添加新标签也毫不费力地工作)。

但是如果你有一对多或多对多关系的关系的mappedBy任务的实体(即任务是“逆”的一面),你需要做更多的工作坚持正确的删除标签。

在这种情况下,您可以修改控制器删除删除标签上的关系。这个假设您有一些编辑()行动是处理你的任务的“更新”:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
/ / src /控制器/ TaskController.php/ /……使用应用程序\实体\任务;使用学说\常见的\集合\ArrayCollection;TaskController扩展AbstractController{公共函数编辑(美元id,请求美元请求,EntityManagerInterface美元entityManager):响应{如果(= = =美元任务=美元entityManager- >getRepository(任务::类)- >找到(美元id)){美元- >createNotFoundException (“没有发现任务id”美元id);}美元originalTags=ArrayCollection ();/ /创建一个ArrayCollection当前标签对象在数据库中foreach(美元任务- >getTags ()作为美元标签){美元originalTags- >add (美元标签);}美元editForm=美元- >createForm (TaskType::类,美元任务);美元editForm- >handleRequest (美元请求);如果(美元editForm- >isSubmitted () & &美元editForm- >isValid ()) {/ /删除标记和任务之间的关系foreach(美元originalTags作为美元标签){如果(= = =美元任务- >getTags ()- >包含(美元标签)){/ /删除任务的标记美元标签- >getTasks ()- >removeElement (美元任务);/ /如果是多对一的关系,消除这样的关系/ / $标签- > setTask(空);美元entityManager- >persist (美元标签);/ /如果你想完全删除标签,你也可以这样做/ / $ entityManager - >删除($标签);}}美元entityManager- >persist (美元任务);美元entityManager- >冲洗();/ /重定向回一些编辑页面返回美元- >redirectToRoute (“task_edit”,(“id”= >美元id]);}/ /……呈现某种形式的模板}}

正如您可以看到的,正确地添加和删除元素可能会非常棘手。除非你有一个多对多关系,任务是“拥有”,你需要做额外的工作来确保正确更新的关系(无论你是添加新标签或删除现有的标签)在每个标签对象本身。

这项工作,包括代码示例,许可下Creative Commons冲锋队3.0许可证。