如何使用表单事件动态修改表单
编辑本页警告:您正在浏览的文档欧宝官网下载appob娱乐下载Symfony 5.1,现已不再维护。
读本页的更新版本用于Syob娱乐下载mfony 6.2(当前稳定版本)。
如何使用表单事件动态修改表单
通常情况下,表单是不能静态创建的。在本文中,您将学习如何基于三个常见的用例定制表单:
例如:您有一个“Product”表单,需要根据正在编辑的底层Product上的数据修改/添加/删除字段。
示例:您创建了一个“好友消息”表单,需要构建一个下拉列表,其中仅包含与当前的经过身份验证的用户。
例如:在注册表单上,您有一个“country”字段和一个“state”字段,它们应该根据“country”字段中的值动态填充。
如果您希望了解表单事件背后的更多基础知识,可以查看形成事件欧宝官网下载app文档。
根据基础数据定制表单
在开始动态表单生成之前,请记住一个裸表单类是什么样的:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/ / src /形式/类型/ ProductType.php名称空间应用程序\形式\类型;使用应用程序\实体\产品;使用ob娱乐下载\组件\形式\AbstractType;使用ob娱乐下载\组件\形式\FormBuilderInterface;使用ob娱乐下载\组件\OptionsResolver\OptionsResolver;类ProductType扩展AbstractType{公共函数buildForm(FormBuilderInterface$构建器数组,$选项):无效{$构建器->add (“名字”);$构建器->add (“价格”);}公共函数configureOptions(OptionsResolver$解析器):无效{$解析器->setDefaults ([“data_class”= >产品::类,]);}}
请注意
如果您还不熟悉这段代码,那么您可能需要退一步,首先查看形式的文章在继续之前。
假设这个表单使用了一个假想的“Product”类,这个类只有两个属性(“name”和“price”)。从这个类生成的表单无论是否正在创建一个新的Product或是否正在编辑一个现有的产品(例如从数据库获取的产品)看起来都是完全相同的。
假设现在,您不希望用户能够更改的名字
值。要做到这一点,您可以依赖Symfonyob娱乐下载EventDispatcher组件系统用于分析对象上的数据并根据Product对象的数据修改表单。在本文中,您将学习如何将这种级别的灵活性添加到表单中。
向表单类添加事件侦听器
所以,不直接加的名字
小部件,创建特定字段的责任被委托给事件监听器:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20
/ / src /形式/类型/ ProductType.php名称空间应用程序\形式\类型;/ /……使用ob娱乐下载\组件\形式\FormEvent;使用ob娱乐下载\组件\形式\FormEvents;类ProductType扩展AbstractType{公共函数buildForm(FormBuilderInterface$构建器数组,$选项):无效{$构建器->add (“价格”);$构建器->addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent$事件){/ /……如果需要,添加名称字段});}/ /……}
目标是创建一个的名字
场只有如果潜在的产品
对象是新的(例如没有持久化到数据库中)。基于此,事件监听器可能看起来像下面这样:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ /……公共函数buildForm(FormBuilderInterface$构建器数组,$选项):无效{/ /……$构建器->addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent$事件){$产品=$事件->getData ();$形式=$事件->getForm ();//检查Product对象是否为new//如果没有数据传递到表单,则数据为"null"。//这应该被认为是一个新的"Product"如果(!$产品||零= = =$产品->getId ()) {$形式->add (“名字”, TextType::类);}});}
请注意
的FormEvents: PRE_SET_DATA
Line实际上解析为字符串form.pre_set_data
.FormEvents服务于组织目的。它是一个集中的位置,您可以在其中找到所有可用的各种表单事件。控件可以查看表单事件的完整列表FormEvents类。
向表单类添加事件订阅器
对象的可重用性更好,或者如果事件侦听器中有一些沉重的逻辑,您还可以移动用于创建对象的逻辑的名字
字段到事件订阅者:
12 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;使用ob娱乐下载\组件\EventDispatcher\EventSubscriberInterface;使用ob娱乐下载\组件\形式\扩展\核心\类型\TextType;使用ob娱乐下载\组件\形式\FormEvent;使用ob娱乐下载\组件\形式\FormEvents;类AddNameFieldSubscriber实现了EventSubscriberInterface{公共静态函数getSubscribedEvents():数组{//告诉dispatcher你想要监听form.pre_set_data//事件和preSetData方法应该被调用。返回[FormEvents::PRE_SET_DATA = >“preSetData”];}公共函数preSetData(FormEvent$事件):无效{$产品=$事件->getData ();$形式=$事件->getForm ();如果(!$产品||零= = =$产品->getId ()) {$形式->add (“名字”, TextType::类);}}}
太棒了!现在在你的表单类中使用它:
12 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 ());}/ /……}
如何基于用户数据动态生成表单
有时,您希望动态生成表单,不仅基于表单中的数据,还基于其他内容——比如来自当前用户的一些数据。假设你有一个社交网站,用户只能给网站上标记为朋友的人发消息。在这种情况下,要发送消息的“选择列表”应该只包含当前用户的好友。
创建表单类型
使用事件监听器,你的表单看起来像这样:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/ / src /形式/类型/ FriendMessageFormType.php名称空间应用程序\形式\类型;使用ob娱乐下载\组件\形式\AbstractType;使用ob娱乐下载\组件\形式\扩展\核心\类型\TextareaType;使用ob娱乐下载\组件\形式\扩展\核心\类型\TextType;使用ob娱乐下载\组件\形式\FormBuilderInterface;使用ob娱乐下载\组件\形式\FormEvent;使用ob娱乐下载\组件\形式\FormEvents;类FriendMessageFormType扩展AbstractType{公共函数buildForm(FormBuilderInterface$构建器数组,$选项):无效{$构建器->add (“主题”, TextType::类)->add (“身体”, TextareaType::类);$构建器->addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent$事件){/ /……添加当前应用程序用户的好友选择列表});}}
现在的问题是获取当前用户并创建一个只包含该用户的朋友的选择字段。这可以通过注射安全
Service转换为form类型,这样你就可以得到当前用户对象:
12 3 4 5 6 7 8 9 10 11 12 13 14
使用ob娱乐下载\组件\安全\核心\安全;/ /……类FriendMessageFormType扩展AbstractType{私人$安全;公共函数__construct(安全$安全){$这->安全=$安全;}/ /…….}
定制表单类型
现在你已经有了所有的基础,你可以使用安全助手的特性来填充监听器逻辑:
12 34 56 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;使用ob娱乐下载\桥\学说\形式\类型\EntityType;使用ob娱乐下载\组件\形式\扩展\核心\类型\TextareaType;使用ob娱乐下载\组件\形式\扩展\核心\类型\TextType;使用ob娱乐下载\组件\安全\核心\安全;/ /……类FriendMessageFormType扩展AbstractType{私人$安全;公共函数__construct(安全$安全){$这->安全=$安全;}公共函数buildForm(FormBuilderInterface$构建器数组,$选项):无效{$构建器->add (“主题”, TextType::类)->add (“身体”, TextareaType::类);//获取用户,快速检查是否存在$用户=$这->安全->getUser ();如果(!$用户) {扔新\ LogicException (没有经过身份验证的用户,FriendMessageFormType不能使用!);}$构建器->addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent$事件)使用($用户){如果(零= = !$事件->getData ()->getFriend ()) {//我们不需要添加friend字段,因为//该消息将被发送给固定的朋友返回;}$形式=$事件->getForm ();$formOptions= (“类”= >用户::类,“choice_label”= >“fullName”,“query_builder”= >函数(UserRepository$userRepository)使用($用户){//在存储库上调用一个返回查询生成器的方法//返回$userRepository->createFriendsQueryBuilder($user);});//创建字段,这类似于$builder->add()//字段名称,字段类型,字段选项$形式->add (“朋友”, EntityType::类,$formOptions);});}/ /……}
请注意
你可能会想,现在你可以访问用户
对象,为什么不直接用在buildForm ()
并忽略事件侦听器?这是因为这样做buildForm ()
方法将导致修改整个表单类型,而不仅仅是修改这个表单实例。这通常不是问题,但从技术上讲,单个表单类型可以用于单个请求来创建多个表单或字段。
使用表单
如果你在用默认的服务。yaml的配置,您的表单可以使用了自动装配而且可以使用autoconfigure.否则,将表单类注册为服务而且标记它与form.type
标签。
在控制器中,像往常一样创建表单:
12 3 4 5 6 7 8 9 10 11 12 13
使用ob娱乐下载\包\FrameworkBundle\控制器\AbstractController;使用ob娱乐下载\组件\HttpFoundation\请求;使用ob娱乐下载\组件\HttpFoundation\响应;类FriendMessageController扩展AbstractController{公共函数新(请求$请求):响应{$形式=$这->createForm (FriendMessageFormType::类);/ /……}}
你也可以将表单类型嵌入到另一个表单中:
1 2 3 4 5
//在一些其他的“表单类型”类公共函数buildForm(FormBuilderInterface$构建器数组,$选项):无效{$构建器->add (“消息”, FriendMessageFormType::类);}
提交表单的动态生成
另一种可能出现的情况是,您希望定制特定于用户提交的数据的表单。例如,假设您有一个体育聚会的注册表单。有些事件将允许您指定您在字段中的首选位置。这是一个选择
字段为例。然而,可能的选择将取决于每一项运动。足球会有进攻、防守、守门员等等……棒球会有投手,但不会有守门员。您需要正确的选项才能通过验证。
meetup作为一个实体字段传递给表单。所以我们可以这样看待每一项运动:
12 34 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
/ / src /形式/类型/ SportMeetupType.php名称空间应用程序\形式\类型;使用ob娱乐下载\桥\学说\形式\类型\EntityType;使用ob娱乐下载\组件\形式\AbstractType;使用ob娱乐下载\组件\形式\FormBuilderInterface;使用ob娱乐下载\组件\形式\FormEvent;使用ob娱乐下载\组件\形式\FormEvents;/ /……类SportMeetupType扩展AbstractType{公共函数buildForm(FormBuilderInterface$构建器数组,$选项):无效{$构建器->add (“运动”, EntityType::类,“类”= >“应用程序实体\ \运动”,“占位符”= >”,]);$构建器->addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent$事件){$形式=$事件->getForm ();//这将是你的实体,即SportMeetup$数据=$事件->getData ();$体育运动=$数据->getSport ();$职位=零= = =$体育运动?[]:$体育运动->getAvailablePositions ();$形式->add (“位置”, EntityType::类,“类”= >“App \实体\位置”,“占位符”= >”,“选择”= >$职位]);});}/ /……}
当您构建这个表单第一次显示给用户时,这个示例可以完美地工作。
但是,在处理表单提交时,事情变得更加困难。这是因为PRE_SET_DATA
事件告诉我们你开始使用的数据(例如一个空的SportMeetup
对象),不提交的数据。
在表单上,我们通常可以听到以下事件:
PRE_SET_DATA
POST_SET_DATA
PRE_SUBMIT
提交
POST_SUBMIT
关键是加aPOST_SUBMIT
监听新字段所依赖的字段。如果你加上POST_SUBMIT
窗体子窗体的侦听器(例如。体育运动
),并向父表单添加新的子表单,form组件将自动检测新字段并将其映射到提交的客户端数据。
该类型现在看起来像:
12 34 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
/ / src /形式/类型/ SportMeetupType.php名称空间应用程序\形式\类型;使用应用程序\实体\体育运动;使用ob娱乐下载\桥\学说\形式\类型\EntityType;使用ob娱乐下载\组件\形式\FormInterface;/ /……类SportMeetupType扩展AbstractType{公共函数buildForm(FormBuilderInterface$构建器数组,$选项):无效{$构建器->add (“运动”, EntityType::类,“类”= >“应用程序实体\ \运动”,“占位符”= >”,]);$formModifier=函数(FormInterface$形式、运动$体育运动= null){$职位=零= = =$体育运动?[]:$体育运动->getAvailablePositions ();$形式->add (“位置”, EntityType::类,“类”= >“App \实体\位置”,“占位符”= >”,“选择”= >$职位]);};$构建器->addEventListener (FormEvents::PRE_SET_DATA,函数(FormEvent$事件)使用($formModifier){//这将是你的实体,即SportMeetup$数据=$事件->getData ();$formModifier($事件->getForm (),$数据->getSport ());});$构建器->get (“运动”)->addEventListener (FormEvents::POST_SUBMIT,函数(FormEvent$事件)使用($formModifier){//这里很重要的是获取$event->getForm()->getData(),作为// $event->getData()将为您提供客户端数据(即ID)$体育运动=$事件->getForm ()->getData ();//由于我们已经将监听器添加到子对象中,所以我们必须传递//回调函数的父函数!$formModifier($事件->getForm ()->getParent (),$体育运动);});}/ /……}
您可以看到,您需要侦听这两个事件并使用不同的回调,这仅仅是因为在两个不同的场景中,您可以使用的数据在不同的事件中可用。除此之外,侦听器总是在给定的表单上执行完全相同的操作。
提示
的FormEvents: POST_SUBMIT
事件不允许修改侦听器绑定的窗体,但允许修改其父窗体。
仍然缺少的一部分是在选择运动后对表单进行客户端更新。这应该通过对应用程序进行AJAX回调来处理。假设你有一个运动聚会创建控制器:
12 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
/ / src /控制器/ MeetupController.php名称空间应用程序\控制器;使用应用程序\实体\SportMeetup;使用应用程序\形式\类型\SportMeetupType;使用ob娱乐下载\包\FrameworkBundle\控制器\AbstractController;使用ob娱乐下载\组件\HttpFoundation\请求;使用ob娱乐下载\组件\HttpFoundation\响应;/ /……类MeetupController扩展AbstractController{公共函数创建(请求$请求):响应{$meetup=新SportMeetup ();$形式=$这->createForm (SportMeetupType::类,$meetup);$形式->handleRequest ($请求);如果($形式->isSubmitted () & &$形式->isValid ()) {/ /……保存聚会,重定向等等。}返回$这->呈现(“meetup / create.html.twig”, (“形式”= >$形式->createView ()));}/ /……}
相关联的模板使用一些JavaScript来更新位置
属性中的当前所选内容体育运动
字段:
12 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)}}{{form_row(form.sport)}}{#
提交整个表单的主要好处是只提取更新后的内容位置
字段是不需要额外的服务器端代码;上面生成提交表单的所有代码都可以重用。