如何使用表单动态修改表单事件吗
编辑该页面警告:你浏览的文档欧宝官网下载appob娱乐下载Symfony 2.3,不再维护。
读这个页面的更新版本Symfob娱乐下载ony 6.3(当前的稳定版本)。
如何使用表单动态修改表单事件吗
通常,一种不能创建静态。在这个条目,您将学习如何定制表单基于三种常见的用例:
-
- 例如:你有一个“产品”形式和需要修改/添加/删除一个字段
- 基于底层产品正在编辑的数据。
例子:创建一个“朋友消息”的形式,需要建立一个下拉,只包含用户的朋友当前的经过身份验证的用户。
例子:在注册表单,你有一个“国家”字段和一个“状态”字段应该根据值动态填充的“国家”。
如果你想了解更多关于事件背后的基本形式,您可以看一下形成事件欧宝官网下载app文档。
基于底层数据的定制表单
之前进入动态表单生成,保持和回忆裸露的形式类是什么样子:
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
/ / src / AppBundle / /类型/ ProductType.php形式名称空间AppBundle\形式\类型;使用ob娱乐下载\组件\形式\AbstractType;使用ob娱乐下载\组件\形式\FormBuilderInterface;使用ob娱乐下载\组件\OptionsResolver\OptionsResolverInterface;类ProductType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“名字”);美元构建器- >add (“价格”);}公共函数setDefaultOptions(OptionsResolverInterface美元解析器){美元解析器- >setDefaults (数组(“data_class”= >“AppBundle \实体\产品”));}公共函数getName(){返回“产品”;}}
请注意
如果这个特定部分的代码不熟悉你,你可能需要退一步,首先检查章形式在继续之前。
假定现在这种形式利用一个虚构的“产品”类,只有两个属性(“name”和“价格”)。形式从这个类会生成相同的无论如果正在创建一个新产品或现有产品正在编辑(如从数据库中取出的产品)。
现在,假设你不希望用户能够改变的名字
一旦创建了对象的值。要做到这一点,你可以依靠Symfony的ob娱乐下载EventDispatcher组件系统分析对象和修改表单上的数据基于产品对象的数据。在这个条目,您将学习如何向表单添加这种程度的灵活性。
向表单添加一个事件监听器类
因此,而不是直接补充说的名字
小部件,创建特定领域的责任委派给一个事件监听器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/ / src / AppBundle / /类型/ ProductType.php形式名称空间AppBundle\形式\类型;/ /……使用ob娱乐下载\组件\形式\FormEvent;使用ob娱乐下载\组件\形式\FormEvents;类ProductType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“价格”);美元构建器- >addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent美元事件){/ /……如果需要添加名称字段});}/ /……}
我们的目标是创建一个的名字
场只有如果底层产品
对象是新的(如尚未持久化到数据库)。在此基础上,事件监听器的样子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ /……公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){/ /……美元构建器- >addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent美元事件){美元产品=美元事件- >getData ();美元形式=美元事件- >getForm ();/ /检查是否“新”产品对象/ /如果没有传递到数据形式,数据是“零”。/ /这应该考虑一个新的“产品”如果(!美元产品| |零= = =美元产品- >getId ()) {美元形式- >add (“名字”,“文本”);}});}
2.2
能够传递一个字符串FormInterface:添加是在Symfony 2.2中引入的。ob娱乐下载
请注意
的FormEvents: PRE_SET_DATA
行解析字符串form.pre_set_data
。FormEvents是一个组织的目的。它是一个集中的位置,你可以找到所有可用的各种形式活动。您可以查看事件通过形式的完整列表FormEvents类。
向表单添加事件订阅者类
为了更好的可重用性,或者有一些沉重的逻辑事件侦听器,您还可以创建的逻辑的名字
场的事件订阅者:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/ / src / AppBundle / /类型/ ProductType.php形式名称空间AppBundle\形式\类型;/ /……使用AppBundle\形式\EventListener\AddNameFieldSubscriber;类ProductType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“价格”);美元构建器- >addEventSubscriber (新AddNameFieldSubscriber ());}/ /……}
现在创建的逻辑的名字
场驻留在它自己的用户类:
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 / AppBundle /形式/ EventListener / AddNameFieldSubscriber.php名称空间AppBundle\形式\EventListener;使用ob娱乐下载\组件\形式\FormEvent;使用ob娱乐下载\组件\形式\FormEvents;使用ob娱乐下载\组件\EventDispatcher\EventSubscriberInterface;类AddNameFieldSubscriber实现了EventSubscriberInterface{公共静态函数getSubscribedEvents(){/ /告诉调度员form.pre_set_data你想听/ /事件,preSetData方法应该调用。返回数组(FormEvents::PRE_SET_DATA = >“preSetData”);}公共函数preSetData(FormEvent美元事件){美元产品=美元事件- >getData ();美元形式=美元事件- >getForm ();如果(!美元产品| |零= = =美元产品- >getId ()) {美元形式- >add (“名字”,“文本”);}}}
如何动态生成形式根据用户数据
有时你想要一种动态生成不仅基于表单的数据还有别的东西,比如一些数据从当前用户。假设您有一个社交网站,用户可以只有消息标记为朋友在网站上。在这种情况下,“选择名单”的消息应该只包含用户的当前用户的朋友。
创建表单类型
使用一个事件侦听器,表单可能看起来像这样:
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
/ / src / AppBundle / /类型/ FriendMessageFormType.php形式名称空间AppBundle\形式\类型;使用ob娱乐下载\组件\形式\AbstractType;使用ob娱乐下载\组件\形式\FormBuilderInterface;使用ob娱乐下载\组件\形式\FormEvents;使用ob娱乐下载\组件\形式\FormEvent;使用ob娱乐下载\组件\安全\核心\SecurityContext;使用ob娱乐下载\组件\OptionsResolver\OptionsResolverInterface;类FriendMessageFormType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“主题”,“文本”)- >add (“身体”,“文本区域”);美元构建器- >addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent美元事件){/ /……添加一个选择当前应用程序用户的好友列表});}公共函数getName(){返回“app_friend_message”;}公共函数setDefaultOptions(OptionsResolverInterface美元解析器){}}
现在的问题是当前用户,创建一个选择字段只包含该用户的朋友。
幸运的是它很容易注入服务的形式。可以在构造函数中:
1 2 3 4 5 6
私人美元securityContext;公共函数__construct(SecurityContext美元securityContext){美元这- >securityContext =美元securityContext;}
请注意
您可能想知道,现在你可以访问用户(通过安全上下文),为什么不直接使用它buildForm
省略的事件监听器呢?这是因为这样做的buildForm
方法将导致整个表单类型被修改,而不仅仅是这一个表单实例。这可能不会成为一个问题,但技术上单个表单类型可以用于单个请求创建多种形式或字段。
自定义表单类型
现在你所有的基础知识在你可以利用的地方SecurityContext
并填写侦听器逻辑:
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 53 54 55 56 57
/ / src / AppBundle / FormType / FriendMessageFormType.php使用ob娱乐下载\组件\安全\核心\SecurityContext;使用学说\ORM\EntityRepository;/ /……类FriendMessageFormType扩展AbstractType{私人美元securityContext;公共函数__construct(SecurityContext美元securityContext){美元这- >securityContext =美元securityContext;}公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“主题”,“文本”)- >add (“身体”,“文本区域”);/ /获取用户,做一个快速的检查,一个存在美元用户=美元这- >securityContext- >getToken ()- >getUser ();如果(!美元用户){扔新\ LogicException (“FriendMessageFormType没有经过身份验证的用户不能使用了!”);}美元构建器- >addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent美元事件)使用(美元用户){美元形式=美元事件- >getForm ();美元formOptions=数组(“类”= >“实体AppBundle \ \用户”,“属性”= >“fullName”,“query_builder”= >函数(EntityRepository美元呃)使用(美元用户){/ /构建一个定制的查询/ /返回$ er - > createQueryBuilder (u) - > addOrderBy (fullName, DESC);/ /存储库或调用一个方法,返回查询生成器/ / er美元是你UserRepository的实例/ /返回$ er - > createOrderByFullNameQueryBuilder ();});/ /创建字段,这是类似$建设者- >添加()/ /字段名、字段类型、数据选项美元形式- >add (“朋友”,“实体”,美元formOptions);});}/ /……}
请注意
的多个
和扩大
表单选项将缺省为false,因为朋友的类型字段实体
。
使用表单
我们现在的形式是可以使用,有两种可能的方法来使用它内部的控制器:
一)手动创建它,记得要将安全上下文传递给它;
或
b)作为服务定义它。
手动创建表单
这是非常简单的,可能是一种更好的方法,除非你使用你的新表单类型在许多地方或将它嵌入到其他形式:
1 2 3 4 5 6 7 8 9 10 11 12
类FriendMessageController扩展控制器{公共函数newAction(请求美元请求){美元securityContext=美元这- >容器- >get (“security.context”);美元形式=美元这- >createForm (新FriendMessageFormType (美元securityContext));/ /……}}
b)定义作为一种服务形式
将表单定义为一种服务,就创建一个正常的服务,然后标记依赖注入的标签。
1 2 3 4 5 6 7
# app / config / config.yml服务:app.form.friend_message:类:AppBundle \ \ \ FriendMessageFormType型形式参数:(“@security.context”)标签:- - - - - -{名称:form.type,别名:app_friend_message}
1 2 3 4 5 6 7
< !- - - - - -- - - - - -app/config/config.xml -->< /span><服务><服务id=“app.form.friend_message”类=“AppBundle \ \ \ FriendMessageFormType型”><论点类型=“服务”id=“security.context”/ ><标签的名字=“form.type”别名=“app_friend_message”/ >< /服务>< /服务>
1 2 3 4 5 6 7 8 9 10
/ / app / config / config . php使用ob娱乐下载\组件\DependencyInjection\参考;美元定义=新定义(“AppBundle \ \类型\ FriendMessageFormType形式”,数组(新引用(“security.context”)));美元定义- >addTag (“form.type”,数组(“别名”= >“app_friend_message”));美元容器- >setDefinition (“app.form.friend_message”,美元定义);
如果你想创建它从内部的服务访问工厂,然后使用:
1
美元形式=美元formFactory- >创建(“friend_message”);
在一个扩展了的控制器控制器类,你可以简单地调用:
1 2 3 4 5 6 7 8 9 10 11
使用ob娱乐下载\包\FrameworkBundle\控制器\控制器;类FriendMessageController扩展控制器{公共函数newAction(请求美元请求){美元形式=美元这- >createForm (“app_friend_message”);/ /……}}
您还可以方便地嵌入表单类型为另一种形式:
1 2 3 4 5
/ /内部其他“表单类型”类公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“消息”,“app_friend_message”);}
动态生成提交的表单
会出现另一种情况是你想定制表单的用户提交的数据。例如,假设你有一个注册表单为运动集会。一些事件将允许您指定您的首选位置。这将是一个选择
领域为例。然而,可能的选择将取决于每个运动。足球会攻击,防御,门将等等……棒球投手,但将没有一个门将。你需要正确的选择为了验证通过。
meetup是作为一个实体字段传递形式。所以我们可以访问每个运动是这样的:
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
/ / src / AppBundle / /类型/ SportMeetupType.php形式名称空间AppBundle\形式\类型;使用ob娱乐下载\组件\形式\AbstractType;使用ob娱乐下载\组件\形式\FormBuilderInterface;使用ob娱乐下载\组件\形式\FormEvent;使用ob娱乐下载\组件\形式\FormEvents;/ /……类SportMeetupType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“运动”,“实体”,数组(“类”= >“AppBundle:运动”,“empty_value”= >”));美元构建器- >addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent美元事件){美元形式=美元事件- >getForm ();/ /这是你的实体,即SportMeetup美元数据=美元事件- >getData ();美元体育运动=美元数据- >getSport ();美元职位=零= = =美元体育运动吗?数组():美元体育运动- >getAvailablePositions ();美元形式- >add (“位置”,“实体”,数组(“类”= >“AppBundle:位置”,“empty_value”= >”,“选择”= >美元职位));});}/ /……}
当你建立这种形式显示给用户的第一次,那么这个作品完美的例子。
然而,事情变得更加困难,当你处理表单提交。这是因为PRE_SET_DATA
事件告诉我们你开始的数据(例如,一个空SportMeetup
对象),不提交的数据。
在一个表单,我们通常可以听以下事件:
PRE_SET_DATA
POST_SET_DATA
PRE_SUBMIT
提交
POST_SUBMIT
2.3
的事件PRE_SUBMIT
,提交
和POST_SUBMIT
在Symfony 2.3中引入的。ob娱乐下载之前,他们命名PRE_BIND
,绑定
和POST_BIND
。
2.2.6款
的行为POST_SUBMIT
事件2.2.6略有改变。下面的例子使用。
关键是要添加一个POST_SUBMIT
侦听器领域,取决于您的新领域。如果您添加一个POST_SUBMIT
孩子一种形式(如侦听器。体育运动
),并添加新的孩子父窗体,表单组件会自动检测到新的领域并将其映射到客户端提交数据。
现在的类型将会看起来像:
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 53 54
/ / src / AppBundle / /类型/ SportMeetupType.php形式名称空间AppBundle\形式\类型;/ /……使用ob娱乐下载\组件\形式\FormInterface;使用AppBundle\实体\体育运动;类SportMeetupType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >add (“运动”,“实体”,数组(“类”= >“AppBundle:运动”,“empty_value”= >”));;美元formModifier=函数(FormInterface美元形式、运动美元体育运动= null){美元职位=零= = =美元体育运动吗?数组():美元体育运动- >getAvailablePositions ();美元形式- >add (“位置”,“实体”,数组(“类”= >“AppBundle:位置”,“empty_value”= >”,“选择”= >美元职位));};美元构建器- >addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent美元事件)使用(美元formModifier){/ /这是你的实体,即SportMeetup美元数据=美元事件- >getData ();美元formModifier(美元事件- >getForm (),美元数据- >getSport ());});美元构建器- >get (“运动”)- >addEventListener (FormEvents::POST_SUBMIT,函数(FormEvent美元事件)使用(美元formModifier){/ /这里重要的获取$事件- > getForm () - > getData ()/ / $事件- > getData()会让你客户数据(即ID)美元体育运动=美元事件- >getForm ()- >getData ();/ /因为我们将侦听器添加到孩子,我们将不得不转嫁/ /回调函数的父!美元formModifier(美元事件- >getForm ()- >getParent (),美元体育运动);});}/ /……}
你可以看到,你需要这两个事件和监听有不同的回调函数在两个不同的场景,只是因为中可用的数据,您可以使用不同的事件。除此之外,听众总是对一个给定的形式执行完全相同的事情。
失踪的一块是运动后的客户端更新表单被选中。这应该是通过一个AJAX调用处理您的应用程序。假设您有一个运动meetup创建控制器:
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 / AppBundle /控制器/ MeetupController.php名称空间AppBundle\控制器;使用ob娱乐下载\包\FrameworkBundle\控制器\控制器;使用ob娱乐下载\组件\HttpFoundation\请求;使用AppBundle\实体\SportMeetup;使用AppBundle\形式\类型\SportMeetupType;/ /……类MeetupController扩展控制器{公共函数createAction(请求美元请求){美元meetup=新SportMeetup ();美元形式=美元这- >createForm (新SportMeetupType (),美元meetup);美元形式- >handleRequest (美元请求);如果(美元形式- >isValid ()) {/ /……保存meetup,重定向等。}返回美元这- >呈现(“AppBundle: Meetup: create.html.twig”,数组(“形式”= >美元形式- >createView ()));}/ /……}
使用一些JavaScript更新相关的模板位置
根据当前选择的表单字段体育运动
字段:
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
{# app /资源/视图/聚会/ create.html。树枝#}{{form_start(形式)}}{{form_row (form.sport)}}{# <选择id =“meetup_sport”…#}{{form_row (form.position)}}{# <选择id =“meetup_position”…#}{#……#}{{form_end(形式)}}<脚本>var运动= $(美元“# meetup_sport”);/ /当运动被选中……sport.change美元(函数(){/ /……检索相应的形式。var= $(美元这).closest (“形式”);/ /模拟表单数据,但只包括所选择的体育价值。var数据= {};数据(sport.attr美元(“名字”)= $ sport.val ();/ /通过AJAX表单提交数据的行动路径。. ajax({美元url:$ form.attr (“行动”),类型:$ form.attr (“方法”),数据:数据,成功:函数(html){/ /替换当前位置的领域……$ (“# meetup_position”).replaceWith (/ /……返回一个来自AJAX响应。美元(html) (“# meetup_position”));/ /位置字段现在显示适当的位置。}});});< /脚本>
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
< !- - - - - -- - - - - -app/Resources/views/Meetup/create.html.php -->< /span>< ? php回声美元视图(“形式”]- >开始(美元形式)? >< ? php回声美元视图(“形式”]- >行(美元形式(“运动”])? >< !- - - - - -- - - - - -
提交整个表单的主要好处是提取更新位置
不需要额外的服务器端代码字段;从上面的所有代码生成提交的表单可以重用。
抑制表单验证
压制表单验证可以使用POST_SUBMIT
事件,防止ValidationListener被称为。
需要这样做的原因是,即使你设置validation_groups
来假
仍然有一些执行完整性检查。例如一个上传文件仍将检查表单是否太大,还是检查不存在字段提交。禁用所有这一切,用一个侦听器:
1 2 3 4 5 6 7 8 9 10 11 12
使用ob娱乐下载\组件\形式\FormBuilderInterface;使用ob娱乐下载\组件\形式\FormEvents;使用ob娱乐下载\组件\形式\FormEvent;公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项){美元构建器- >addEventListener (FormEvents::POST_SUBMIT,函数(FormEvent美元事件){美元事件- >stopPropagation ();},900年);/ /总比ValidationListener设定一个更高的优先级/ /……}
谨慎
通过这样做,你可能会不小心禁用不仅仅是表单验证的东西,因为POST_SUBMIT
事件可能有其他听众。