如何使用表单动态修改表单事件吗

编辑该页面

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

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

如何使用表单动态修改表单事件吗

通常,一种不能创建静态。在本文中,您将学习如何定制表单基于三种常见的用例:

  1. 基于底层数据的定制表单

    例如:你有一个“产品”的形式和需要修改/添加/删除字段根据底层产品正在编辑的数据。

  2. 如何动态生成形式根据用户数据

    例子:创建一个“朋友消息”的形式,需要建立一个下拉,只包含用户的朋友当前的经过身份验证的用户。

  3. 动态生成提交的表单

    例子:在注册表单,你有一个“国家”字段和一个“状态”字段应该根据值动态填充的“国家”。

如果你想了解更多关于事件背后的基本形式,您可以看一下形成事件欧宝体育电话文档。

基于底层数据的定制表单

从动态表单生成之前,记住裸露的形式类是什么样子:

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

请注意

如果这个特定部分的代码不熟悉你,你可能需要退一步,首先检查形式的文章在继续之前。

假定现在这种形式利用一个虚构的“产品”类,只有两个属性(“name”和“价格”)。形式从这个类会生成相同的无论如果正在创建一个新产品或现有产品正在编辑(如从数据库中取出的产品)。

现在,假设你不希望用户能够改变的名字一旦创建了对象的值。要做到这一点,你可以依靠Symfony的欧宝娱乐app下载地址EventDispatcher组件系统分析对象和修改表单上的数据基于产品对象的数据。在本文中,您将学习如何向表单添加这种程度的灵活性。

向表单添加一个事件监听器类

因此,而不是直接补充说的名字小部件,创建特定领域的责任委派给一个事件监听器:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/ / src /形式/类型/ ProductType.php名称空间应用程序\形式\类型;/ /……使用欧宝娱乐app下载地址\组件\形式\FormEvent;使用欧宝娱乐app下载地址\组件\形式\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 (“名字”,TextType::类);}});}

请注意

FormEvents: PRE_SET_DATA行解析字符串form.pre_set_dataFormEvents是一个组织的目的。它是一个集中的位置,你可以找到所有可用的各种形式活动。您可以查看事件通过形式的完整列表FormEvents类。

向表单添加事件订阅者类

为了更好的可重用性,或者有一些沉重的逻辑事件侦听器,您还可以创建的逻辑的名字场的事件订阅者:

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 /形式/ EventListener / AddNameFieldSubscriber.php名称空间应用程序\形式\EventListener;使用欧宝娱乐app下载地址\组件\EventDispatcher\EventSubscriberInterface;使用欧宝娱乐app下载地址\组件\形式\扩展\核心\类型\TextType;使用欧宝娱乐app下载地址\组件\形式\FormEvent;使用欧宝娱乐app下载地址\组件\形式\FormEvents;AddNameFieldSubscriber实现了EventSubscriberInterface{公共静态函数getSubscribedEvents():数组{/ /告诉调度员form.pre_set_data你想听/ /事件,preSetData方法应该调用。返回[FormEvents::PRE_SET_DATA = >“preSetData”];}公共函数preSetData(FormEvent美元事件):无效{美元产品=美元事件- >getData ();美元形式=美元事件- >getForm ();如果(!美元产品| |= = =美元产品- >getId ()) {美元形式- >add (“名字”,TextType::类);}}}

太棒了!现在使用的表单类:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/ / src /形式/类型/ ProductType.php名称空间应用程序\形式\类型;/ /……使用应用程序\形式\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日
/ / src /形式/类型/ FriendMessageFormType.php名称空间应用程序\形式\类型;使用欧宝娱乐app下载地址\组件\形式\AbstractType;使用欧宝娱乐app下载地址\组件\形式\扩展\核心\类型\TextareaType;使用欧宝娱乐app下载地址\组件\形式\扩展\核心\类型\TextType;使用欧宝娱乐app下载地址\组件\形式\FormBuilderInterface;使用欧宝娱乐app下载地址\组件\形式\FormEvent;使用欧宝娱乐app下载地址\组件\形式\FormEvents;FriendMessageFormType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项):无效{美元构建器- >add (“主题”,TextType::类)- >add (“身体”,TextareaType::类);美元构建器- >addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent美元事件){/ /……添加一个选择当前应用程序用户的好友列表});}}

现在的问题是当前用户,创建一个选择字段只包含该用户的朋友。可以注射安全服务到形式类型,这样你就可以得到当前用户对象:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
使用欧宝娱乐app下载地址\组件\安全\核心\安全;/ /……FriendMessageFormType扩展AbstractType{私人美元安全;公共函数__construct(安全美元安全){美元- >安全=美元安全;}/ /……。}

自定义表单类型

现在您已经基本到位可以使用所有功能的安全辅助填写侦听器逻辑:

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 58 59 60 61
/ / src /形式/类型/ FriendMessageFormType.php名称空间应用程序\形式\类型;使用应用程序\实体\用户;使用学说\ORM\EntityRepository;使用欧宝娱乐app下载地址\\学说\形式\类型\EntityType;使用欧宝娱乐app下载地址\组件\形式\扩展\核心\类型\TextareaType;使用欧宝娱乐app下载地址\组件\形式\扩展\核心\类型\TextType;使用欧宝娱乐app下载地址\组件\安全\核心\安全;/ /……FriendMessageFormType扩展AbstractType{私人美元安全;公共函数__construct(安全美元安全){美元- >安全=美元安全;}公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项):无效{美元构建器- >add (“主题”,TextType::类)- >add (“身体”,TextareaType::类);/ /获取用户,做一个快速的检查,一个存在美元用户=美元- >安全- >getUser ();如果(!美元用户){\ LogicException (“FriendMessageFormType没有经过身份验证的用户不能使用了!”);}美元构建器- >addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent美元事件)使用(美元用户){如果(= = !美元事件- >getData ()- >getFriend ()) {/ /我们不需要添加字段,因为朋友/ /消息将寄给一个固定的朋友返回;}美元形式=美元事件- >getForm ();美元formOptions= (“类”= >用户::类,“choice_label”= >“fullName”,“query_builder”= >函数(UserRepository美元userRepository)使用(美元用户){/ /调用一个方法返回查询构建器的存储库/ /返回$ userRepository - > createFriendsQueryBuilder ($ user);});/ /创建字段,这是类似$建设者- >添加()/ /字段名、字段类型、字段选项美元形式- >add (“朋友”,EntityType::类,美元formOptions);});}/ /……}

请注意

您可能想知道,现在你可以访问用户对象,为什么不直接使用它buildForm ()省略的事件监听器呢?这是因为这样做的buildForm ()方法将导致整个表单类型被修改,而不仅仅是这一个表单实例。这可能不会成为一个问题,但技术上单个表单类型可以用于单个请求创建多种形式或字段。

使用表单

如果你使用默认的服务。yaml的配置,你的形式是由于使用做好准备自动装配可以使用autoconfigure。否则,注册表单类作为服务标记它form.type标签。

在一个控制器,创建表单像正常:

1 2 3 4 5 6 7 8 9 10 11 12 13
使用欧宝娱乐app下载地址\\FrameworkBundle\控制器\AbstractController;使用欧宝娱乐app下载地址\组件\HttpFoundation\请求;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;FriendMessageController扩展AbstractController{公共函数(请求美元请求):响应{美元形式=美元- >createForm (FriendMessageFormType::类);/ /……}}

您还可以将表单类型嵌入到另一种形式:

1 2 3 4 5
/ /内部其他“表单类型”类公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项):无效{美元构建器- >add (“消息”,FriendMessageFormType::类);}

动态生成提交的表单

会出现另一种情况是你想定制表单的用户提交的数据。例如,假设你有一个注册表单为运动集会。一些事件将允许您指定您的首选位置。这将是一个选择领域为例。然而,可能的选择将取决于每个运动。足球会攻击,防御,门将等等……棒球投手,但将没有一个门将。你需要正确的选择为了验证通过。

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 43 44 45
/ / src /形式/类型/ SportMeetupType.php名称空间应用程序\形式\类型;使用应用程序\实体\位置;使用应用程序\实体\体育运动;使用欧宝娱乐app下载地址\\学说\形式\类型\EntityType;使用欧宝娱乐app下载地址\组件\形式\AbstractType;使用欧宝娱乐app下载地址\组件\形式\FormBuilderInterface;使用欧宝娱乐app下载地址\组件\形式\FormEvent;使用欧宝娱乐app下载地址\组件\形式\FormEvents;/ /……SportMeetupType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项):无效{美元构建器- >add (“运动”,EntityType::类,“类”= >运动::类,“占位符”= >]);美元构建器- >addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent美元事件){美元形式=美元事件- >getForm ();/ /这是你的实体,即SportMeetup美元数据=美元事件- >getData ();美元体育运动=美元数据- >getSport ();美元职位== = =美元体育运动吗?[]:美元体育运动- >getAvailablePositions ();美元形式- >add (“位置”,EntityType::类,“类”= >位置::类,“占位符”= >,“选择”= >美元职位]);});}/ /……}

当你建立这种形式显示给用户的第一次,那么这个作品完美的例子。

然而,事情变得更加困难,当你处理表单提交。这是因为PRE_SET_DATA事件告诉我们你开始的数据(例如,一个空SportMeetup对象),提交的数据。

在一个表单,我们通常可以听以下事件:

  • PRE_SET_DATA
  • POST_SET_DATA
  • PRE_SUBMIT
  • 提交
  • POST_SUBMIT

关键是要添加一个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 55 56
/ / src /形式/类型/ SportMeetupType.php名称空间应用程序\形式\类型;使用应用程序\实体\位置;使用应用程序\实体\体育运动;使用欧宝娱乐app下载地址\\学说\形式\类型\EntityType;使用欧宝娱乐app下载地址\组件\形式\FormInterface;/ /……SportMeetupType扩展AbstractType{公共函数buildForm(FormBuilderInterface美元构建器数组,美元选项):无效{美元构建器- >add (“运动”,EntityType::类,“类”= >运动::类,“占位符”= >]);美元formModifier=函数(FormInterface美元形式、运动美元体育运动= null){美元职位== = =美元体育运动吗?[]:美元体育运动- >getAvailablePositions ();美元形式- >add (“位置”,EntityType::类,“类”= >位置::类,“占位符”= >,“选择”= >美元职位]);};美元构建器- >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 (),美元体育运动);});}/ /……}

你可以看到,你需要这两个事件和监听有不同的回调函数在两个不同的场景,只是因为中可用的数据,您可以使用不同的事件。除此之外,听众总是对一个给定的形式执行完全相同的事情。

提示

FormEvents: POST_SUBMIT事件不允许修改表单绑定到侦听器,但它允许修改。

失踪的一块是运动后的客户端更新表单被选中。这应该是通过一个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 /控制器/ MeetupController.php名称空间应用程序\控制器;使用应用程序\实体\SportMeetup;使用应用程序\形式\类型\SportMeetupType;使用欧宝娱乐app下载地址\\FrameworkBundle\控制器\AbstractController;使用欧宝娱乐app下载地址\组件\HttpFoundation\请求;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;/ /……MeetupController扩展AbstractController{公共函数创建(请求美元请求):响应{美元meetup=SportMeetup ();美元形式=美元- >createForm (SportMeetupType::类,美元meetup);美元形式- >handleRequest (美元请求);如果(美元形式- >isSubmitted () & &美元形式- >isValid ()) {/ /……保存meetup,重定向等。}返回美元- >renderForm (“meetup / create.html.twig”,(“形式”= >美元形式]);}/ /……}

使用一些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
{/聚会/ 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.responseText) (“# meetup_position”));/ /位置字段现在显示适当的位置。}});});< /脚本>

提交整个表单的主要好处是提取更新位置不需要额外的服务器端代码字段;从上面的所有代码生成提交的表单可以重用。

这项工作,包括代码示例,许可下Creative Commons冲锋队3.0许可证。
欧宝娱乐app下载地址Symfony 5.3支持通过JoliCode