如何嵌入一组形式

编辑该页面

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

这个页面的更新版本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 / Acme / / Task.php TaskBundle /实体名称空间Acme\TaskBundle\实体;使用学说\常见的\集合\ArrayCollection;任务{受保护的美元描述;受保护的美元标签;公共函数__construct(){美元- >标签=ArrayCollection ();}公共函数getDescription(){返回美元- >描述;}公共函数setDescription(美元描述){美元- >描述=美元描述;}公共函数getTags(){返回美元- >标签;}}

请注意

ArrayCollection是特定于教条和使用一个基本上是一样的吗数组(但必须是一个ArrayCollection如果你使用原则)。

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

1 2 3 4 5 6 7
/ / src / Acme / / Tag.php TaskBundle /实体名称空间Acme\TaskBundle\实体;标签{公共美元的名字;}

提示

的名字属性是公共的,但它可以很容易被保护或私人(但需要getNamesetName方法)。

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

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日
/ / src / Acme / TaskBundle /形式/类型/ TagType.php名称空间Acme\TaskBundle\形式\类型;使用欧宝娱乐app下载地址\组件\形式\AbstractType;使用欧宝娱乐app下载地址\组件\形式\FormBuilderInterface;使用欧宝娱乐app下载地址\组件\OptionsResolver\OptionsResolverInterface;TagType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“名字”);}公共函数setDefaultOptions(OptionsResolverInterface美元解析器){美元解析器- >setDefaults (数组(“data_class”= >“Acme \ TaskBundle \实体\标签”));}公共函数getName(){返回“标签”;}}

,你有足够的呈现一个标签形式本身。但由于最终的目标是允许的标记任务要修改正确的任务表单内,创建一个表单任务类。

请注意,您嵌入的集合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 / Acme / TaskBundle /形式/类型/ TaskType.php名称空间Acme\TaskBundle\形式\类型;使用欧宝娱乐app下载地址\组件\形式\AbstractType;使用欧宝娱乐app下载地址\组件\形式\FormBuilderInterface;使用欧宝娱乐app下载地址\组件\OptionsResolver\OptionsResolverInterface;TaskType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“描述”);美元构建器- >add (“标签”,“收集”,数组(“类型”= >TagType ()));}公共函数setDefaultOptions(OptionsResolverInterface美元解析器){美元解析器- >setDefaults (数组(“data_class”= >“Acme实体\ TaskBundle \ \任务”));}公共函数getName(){返回“任务”;}}

现在在你的控制器,您将初始化的一个新实例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
/ / src / Acme / TaskBundle /控制器/ TaskController.php名称空间Acme\TaskBundle\控制器;使用Acme\TaskBundle\实体\任务;使用Acme\TaskBundle\实体\标签;使用Acme\TaskBundle\形式\类型\TaskType;使用欧宝娱乐app下载地址\组件\HttpFoundation\请求;使用欧宝娱乐app下载地址\\FrameworkBundle\控制器\控制器;TaskController扩展控制器{公共函数newAction(请求美元请求){美元任务=任务();/ /伪代码——这是这这样的任务有一些标记/ /否则,这并不是一个有趣的例子美元标签1=标签();美元标签1- >name =“标签1”;美元任务- >getTags ()- >add (美元标签1);美元标签2=标签();美元标签2- >name =标签2的;美元任务- >getTags ()- >add (美元标签2);/ /结束伪代码美元形式=美元- >createForm (TaskType (),美元任务);美元形式- >handleRequest (美元请求);如果(美元形式- >isValid ()) {/ /……也许做一些表单处理,像拯救任务和标记对象}返回美元- >呈现(“AcmeTaskBundle:任务:new.html.twig”,数组(“形式”= >美元形式- >createView ()));}}

相应的模板现在能够呈现的描述现场的任务以及所有的形式TagType形式已经与此相关的任何标记任务。在上面的控制器中,我添加了一些伪代码,这样您就可以在行动(因为看到这个任务第一次创建时标记为零)。

  • 嫩枝
  • PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
{# src / Acme / TaskBundle /资源/视图/任务/ 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_level默认的PHP设置One hundred.

这个指令限制递归到100可能不够呼吁呈现表单模板中如果你渲染整个形式(如form_widget(形式))。为了解决这个问题你可以设置这个指令(无论是通过更高的价值php . ini文件或通过报错,例如在app / autoload.php使用手工)或渲染每个表单字段form_row

这种形式不应该包含额外的字段。使它灵活,添加allow_add选择你的收藏领域:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
/ / src / Acme / TaskBundle /形式/类型/ TaskType.php/ /……使用欧宝娱乐app下载地址\组件\形式\FormBuilderInterface;公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“描述”);美元构建器- >add (“标签”,“收集”,数组(“类型”= >TagType (),“allow_add”= >真正的));}

除了告诉现场接受任何数量的提交对象,allow_add也给出了“原型”变量可用。这个“原型”有点“模板”,包含所有的HTML可以呈现任何新的“标签”形式。呈现,进行以下修改你的模板:

  • 嫩枝
  • PHP
1 2 3
<ul=“标签”data-prototype={{form_widget (form.tags.vars.prototype) | e}}>< /ul>

请注意

如果你使你的整个“标签”sub-form(如。form_row (form.tags)),然后上可用的原型是自动外div随着data-prototype属性,类似于上面所看到的内容。

提示

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

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

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

1
<ul=“标签”data-prototype=& lt;div比;& lt;标签类=“;要求“;比;__name__& lt;/标签比;& lt;div id =“;task_tags___name__“;比;& lt;div比;& lt;标签=“;task_tags___name___name“;类=“;要求“;比;的名字& lt;/标签比;& lt;输入类型=“;文本“;id =“;task_tags___name___name“;name =“;任务[标记][__name__][名称]“;要求=“;要求“;最大长度=“;255年“;/比;& lt;/ div比;& lt;/ div比;& lt;/ div比;>

本节的目标是使用JavaScript来读这个属性和动态添加新的标签形式当用户单击“添加一个标签”链接。为了让事情简单,这个示例使用jQuery,假设您已包括页面上的某个地方。

添加一个脚本标签页面上的某个地方,这样你就可以开始写一些JavaScript。

首先,添加一个链接“标签”列表的底部通过JavaScript。第二,绑定到该链接的“点击”活动,这样你就可以添加一个新的标签形式(addTagForm将显示下一个):

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
varcollectionHolder美元;/ /设置一个“添加一个标签”的链接varaddTagLink = $(美元' < a href = " # " class = " add_tag_link " > < / >”添加一个标签);varnewLinkLi = $(美元<李> < /李>的).append (addTagLink美元);jQuery (文档时(函数(){/ /得到的ul标签的集合collectionHolder = $(美元“ul.tags”);/ /添加“添加一个标签”锚和李ul标签(collectionHolder.append美元newLinkLi);/ /计算当前表单输入(如2),用这个新/ /索引时插入一个新项目(例如:2)collectionHolder.data美元(“指数”collectionHolder.find美元(:输入). length);addTagLink.on美元(“点击”,函数(e){/ /防止链接创建一个“#”URLe.preventDefault ();/ /添加一个新的标签形式(见下一个代码块)addTagForm (collectionHolder, newLinkLi美元);});});

addTagForm函数的工作将被使用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 18
函数addTagForm(collectionHolder, newLinkLi美元){/ /得到data-prototype前面介绍的那样var原型= $ collectionHolder.data (“原型”);/ /获取新索引var指数= $ collectionHolder.data (“指数”);/ /替换__name__原型的HTML/ /而不是成为一个基于我们有多少物品的数量varnewForm中将= prototype.replace (/ __name__ / g、指数);/ /增加指数与下一项collectionHolder.data美元(“指数”、索引+1);/ /显示表单的页面在一个李,李在“添加一个标签”链接varnewFormLi = $(美元<李> < /李>的).append (newForm中将);(newLinkLi.before美元newFormLi);}

请注意

最好是单独的JavaScript在现实比把它写在HTML JavaScript文件都在这里完成。

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

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

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

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

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

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

谨慎

如果没有addTagremoveTag找到方法,表单将仍在使用setTag即使by_reference。你会了解更多的removeTag方法在本文的后面。

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

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

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

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

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

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

1 2 3 4 5 6 7 8 9
/ / src / Acme / / Task.php TaskBundle /实体/ /……公共函数addTag(标签美元标签){美元标签- >addTask (美元);美元- >标签- >add (美元标签);}

内部标签,确保你有一个addTask方法:

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

如果你有一对多的关系,那么解决方法是相似的,除了您可以简单地调用setTask从内addTag

allow_delete选择的类型:

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

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

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

allow_delete选项有一个后果:如果一个项目没有发送提交集合,从集合中删除相关数据在服务器上。解决方案是因此删除表单元素的DOM。

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
jQuery (文档时(函数(){/ /得到的ul标签的集合collectionHolder = $(美元“ul.tags”);/ /添加一个删除链接到所有现有的li元素标记形式collectionHolder.find美元(“李”). each (函数(){addTagFormDeleteLink ($ ());});/ /……从上面剩下的块});函数addTagForm(){/ /……/ /添加一个删除链接到新形式addTagFormDeleteLink ($ newFormLi);}

addTagFormDeleteLink函数将看起来像这样:

1 2 3 4 5 6 7 8 9 10 11 12
函数addTagFormDeleteLink(tagFormLi美元){varremoveFormA = $(美元' < a href = " # " >删除这个标签< / > ');(tagFormLi.append美元removeFormA);removeFormA.on美元(“点击”,函数(e){/ /防止链接创建一个“#”URLe.preventDefault ();/ /删除标记的李形式美元tagFormLi.remove ();});}

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

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

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

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

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

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 / Acme / TaskBundle /控制器/ TaskController.php使用学说\常见的\集合\ArrayCollection;/ /……公共函数editAction(美元id,请求美元请求){美元新兴市场=美元- >getDoctrine ()- >getManager ();美元任务=美元新兴市场- >getRepository (“AcmeTaskBundle:任务”)- >找到(美元id);如果(!美元任务){美元- >createNotFoundException (“没有任务发现”美元id);}美元originalTags=ArrayCollection ();/ /创建一个ArrayCollection当前标签对象在数据库中foreach(美元任务- >getTags ()作为美元标签){美元originalTags- >add (美元标签);}美元editForm=美元- >createForm (TaskType (),美元任务);美元editForm- >handleRequest (美元请求);如果(美元editForm- >isValid ()) {/ /删除标记和任务之间的关系foreach(美元originalTags作为美元标签){如果(= = =美元任务- >getTags ()- >包含(美元标签)){/ /删除任务的标记美元标签- >getTasks ()- >removeElement (美元任务);/ /如果是多对一的关系,消除这样的关系/ / $标签- > setTask(空);美元新兴市场- >persist (美元标签);/ /如果你想完全删除标签,你也可以这样做/ / $ em - >删除($标签);}}美元新兴市场- >persist (美元任务);美元新兴市场- >冲洗();/ /重定向回一些编辑页面返回美元- >重定向(美元- >generateUrl (“task_edit”,数组(“id”= >美元id)));}/ /渲染某种形式的模板}

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