节点表单是系统中使用最频繁最重要的表单,她是建立在许多组件和概念之上的,内容广泛,本篇意在起到一个指示和总结作用,帮助想直接操控节点表单的读者明白需要知道些什么,同时就节点表单本身进行介绍,这好似在爬山到了顶上,顶上并没有多少东西,节点表单就是最终的应用,就其本身并没有多少内容,因此本篇会很短,仅是一个综合运用的总结,更多重要内容是在爬山的路上,没有路上的内容将无法到达山顶,因此在阅读本篇前需要先知道许多前置知识,这些本系列已发布,这里整理出来供读者去学习:
《表单API》 :实体表单建立在基础表单之上,本系列发布过多篇表单相关主题,如示例、进阶等,后续还会针对表单具体问题继续发布
《实体表单entity form》 :完成所有实体表单的相同工作,重点讲述通用基类和实体表单运作流程
《实体表单显示EntityFormDisplay》 :管控表单中哪些字段显示,及其如何显示,介绍了表单模式
《字段控件FieldWidget》 :负责单个字段的表单生成、值提取、表单错误显示
《实体表单验证(上下集)》 :结合类型化数据组件验证表单、设置约束
《临时储存与消息服务》 :为节点表单提供预览数据的暂时存储,内容相对简单
节点表单建立在以上这些知识之上,是对她们的综合运用,下文讲述节点表单本身
节点表单设置:
节点添加表单的路由定义在node模块的静态路由定义文件中:
路由名:node.add
控制器为:\Drupal\node\Controller\NodeController::add
路径:'/node/add/{node_type}'
节点编辑、删除路由定义在节点模块的路由提供器中:
提供器类:\Drupal\node\Entity\NodeRouteProvider::getRoutes
路由名:“entity.node.edit_form”和“entity.node.delete_form”
路径分别为:
'/node/{node}/edit'
'/node/{node}/delete'
编辑、删除路由均以通用的“_entity_form”方式设置,添加路由虽然以控制器方式指定,但并没有什么区别
添加和编辑所用表单类(在实体释文中指定,这是本篇重点讲述的内容):
"Drupal\node\NodeForm"
删除表单有两个:
Drupal\node\Form\NodeDeleteForm(直接删除)
Drupal\node\Form\DeleteMultiple(多步确认删除)
得到节点表单:
和通用的实体表单一样,是通过实体表单构建器产生,节点添加表单代码示例:
$node = \Drupal::entityTypeManager()->getStorage('node')->create([
'type' => 'article',
]);
$form = \Drupal::service('entity.form_builder')->getForm($node);
return $form;
在控制器中执行以上代码,这将显示文章节点的添加表单,如果是编辑表单则如下:
$node = \Drupal::entityTypeManager()->getStorage('node')->load(1);
$form = \Drupal::service('entity.form_builder')->getForm($node, 'edit');
return $form;
如果想得到删除表单,仅需将以上代码的‘edit’改为‘delete’即可,getForm方法的第二个参数是表单的操作名,或者称为表单模式,其值是节点实体释文定义中表单处理器的子键名(键值是对应的表单类),改为‘delete’后代码将返回该实体的删除确认表单,注意这不是直接删除实体,而是提供用户操作界面,如需在代码上删除实体应该调用实体对象的删除方法,这两种删除方式区别在于有没有删除日志等
节点表单类:
删除表单比较简单,本篇仅介绍添加和编辑表单,她们采用同一个表单类:
\Drupal\node\NodeForm
这里将该类称为节点表单,她继承自内容实体表单基类,大部分表单功能由基类提供,详见本系列《实体表单entity form》主题,节点表单类则继续提供了预览等功能,对各方法介绍如下:
public static function create(ContainerInterface $container)
实例化表单,因为在父类中实现了容器注入接口,所以节点表单对象被该方法实例化,传入的私有临时储存工厂用于实例化私有临时储存对象以暂时性储存表单预览数据
public function form(array $form, FormStateInterface $form_state)
节点表单会处理添加、编辑、预览返回三种情况,如果进入的url类似如下:
http://www.dp.com/zh-hans/node/add/article?uuid=107fc58d-acf5-4558-ac42-7cab5599e440
则表示当前页面是从预览页面返回的,预览的数据之前已经填写过,以整个表单状态对象的方式被保存在私有临时储存中,以“node_preview”作为集名,以实体的uuid作为键名,注意保存的是之前的表单状态对象,如果是预览返回方式则恢复用户数据并设置表单重建,这里在表单状态对象上设置“has_been_previewed”为true,含义是已经被预览过,这是为了当系统设置为必须预览时,标记已经预览,后续可依据此值来判断是否显示保存按钮。
该方法提供了很多渲染元素,如$form['meta']将显示在表单页面的右上角,菜单等模块也默认添加了表单,这些比较简单不再详述,可参见钩子:
hook_form_BASE_FORM_ID_alter()
具体钩子函数如:
menu_ui_form_node_form_alter
protected function actions(array $form, FormStateInterface $form_state)
在该方法中设置了预览按钮的提交处理器,见下,这里解释一下以下代码:
$node->type->entity->getPreviewMode();
这里$node->type是节点实体的一个字段,属于引用字段,$node->type->entity会执行引用字段对象的魔术方法:
\Drupal\Core\Field\FieldItemList::__get
返回节点类型实体对象(Drupal\node\Entity\NodeType),方法getPreviewMode是节点类型实体对象上的方法,其值是我们在内容类型结构管理中设置的“提交前预览”项的值,通过此值来判断是否显示保存、预览按钮,配置可参考以下地址:
/admin/structure/types/manage/article
该值对应三个常量:
DRUPAL_DISABLED:禁止,值为0
DRUPAL_OPTIONAL:可选,值为1
DRUPAL_REQUIRED:必选,值为2
这些产量定义在系统模块的system.module文件中,在许多地方被使用,我们在开发中遇到类似这样的选择时,为了统一起见,可直接使用这些常量
public function preview(array $form, FormStateInterface $form_state)
该方法是预览按钮的提交处理器,将表单状态对象保存到私有临时存储中,并跳转到预览页面,预览页面的路由是“entity.node.preview”,注意预览过程中节点实体并没有被保存,换句话说就是尚未执行$node->save();方法
public function save(array $form, FormStateInterface $form_state)
执行实体保存,添加日志信息,显示提示消息,删除预览数据;如果保存成功且用户有访问实体视图权限,页面将跳转到视图页面,否则转到首页
作者语:
本篇是云客drupal8源码分析第100篇,也将作为2018-2019春节前的最后一篇发布,该系列总字数已超过60万字,虽取名为源码分析,实则也是开发者教程,可视为与官网文档平行的中文文档项目,完全原创并非翻译,许多时候和官方英文文档也形成互补,官网没有或模糊的知识点可以在这里找到详细解释,内容均来自源代码解读,目前已覆盖了drupal8系统核心大部分知识,还在继续直至全部介绍完成;在这个值得纪念一下的里程碑时刻,想和读者朋友们闲谈三个方面的话题:中文文档贡献、难的潜在价值、drupal发展趋势。
首先是呼吁更多的朋友们加入drupal的中文文档工作,将drupal介绍给中国的初学者,不管您对drupal理解深度如何,也不管写的是什么内容,离散零星的,或者成系列的,我相信一定是有用的,一定会在某一个时刻帮助到某一个人,很多小伙伴把这种行为称为贡献,但我想强调一点,这是双赢的,给大家分享一件云客身边的事,昨晚孩子妈妈带了一个红糖馒头回家,因为只有一个,所以两个还在上幼儿园的孩子一人一半,他们没见过这种馒头,非常高兴,吃的津津有味,我在一旁看着他们发呆,姐姐突然停下来,掰了一块喂过来,两眼闪亮亮的,这是把仅有的半个馒头掰了一块塞到爸爸嘴里,弟弟跟着也这样做了,瞬间感觉很幸福很放心,不是每个小孩都会如此,这让我想起一些事,在送女儿上幼儿园的路上,会经过一座天桥,时常遇见一位四五十岁的乞丐,没有双手盘地而坐,他总是很高兴的大声唱歌,人在清晨遇到一位生活不幸的人还在乐观的高歌,便会燃起对生活的珍惜和对不幸者的同情,因此很多时候会给女儿一点零钱让她给人家拿去,开始她不敢,畏畏缩缩的,次数多了就不怕了,后来会主动找我要钱给人家,时间久了对残疾的不幸理解的也深了,要的也多了,后来我会拉住她,然后告诉她这些钱可以买到什么她想要的东西,如果给了就不能买了,尽管她不舍得,但还是会给,我想正是因为类似这样的事情培养了她的善良,让她可以将不多的馒头分一些放到爸爸嘴里,对于一个爸爸而言,孩子是否善良和勤奋是第一等心事,有这两者长大后他们一定不会差,一个随心的善举,看似没有回报,却解决这么大的一桩心事,这种赠与成了双赢。
小女孩可以为自己觉得该做的事情放弃自己喜欢的玩具零食,这可能是天性,回到成年人身上,为了一些有意义的事情牺牲一点时间,则更多的可能是权衡,我想说这也会得到极大的回报,人若想有所成就,就必须和社会建立协作,在此之前必须让社会认识您,知道您会什么,靠不靠谱,作为开发人员,无疑通过写作技术分享这样的方式是很不错的,让需要您才华的人知道您在哪里,能够找到您,开放自己变得有机会,在这些机会中您比别人更容易找到实行梦想的途径,写作过程中也会夯实自己的知识体系,对不知道的点穷追不舍,因为您写作的内容是要给大众看的,不会允许自己敷衍,不懂装懂、掩耳盗铃是不可能的,这在技术上会让自己成长,成长了机会也来了离梦想还远吗?这些好处都真实发生在云客身上,实际上回报远不止于此,会出现各种间接的良性循环,感动的甚至有读者直接在QQ上给云客发红包,没有任何要求,这种敬意让自己感觉到存在的价值,在这里真诚呼吁大家来写下drupal的方方面面,让她变得更加容易,对于自己而言则无心插柳,柳可能成荫。
提到drupal的难,社区里面有一张drupal学习曲线对比玩笑图,可能把初学者吓的望而生畏,但也可能把勇者挑动的跃跃欲试,在此我想说应该理性的看待这个“难”,drupal既没有图中形容的那么不可征服,当然也不会太简单,她的难是合理的,在云客的互联网开发技术群里,主题文档系列作者晴空用锄头和挖掘机来讨论这个问题,如果您选择了锄头的简单,那么可能您正在修一条小路,如果您欲开山劈地、搭建桥梁隧道那么无论如何您不会去选择锄头的简单,挖机机械才是您需要的,但您需要先去学习它们,在使用上它们相比之下肯定难一些,但这是合理的“难”,其背后往往蕴藏着一些不容易看到的巨大价值,那么drupal的这些不容易看到的价值是什么呢?这里列举其中一个:大型项目的后期成本。
面对项目,如果您选择自己开发一个系统,开始时一定是顺风顺水的,但当规模达到一定程度,需要十几人协作或数百人协作的时候,问题就来了,这个时候系统已发展的很复杂,由于各种原因也会导致初期考虑不足,欠下技术债,后面加入的开发人员必须先专门学习您的系统才能工作,这很难,需要很多时间,您需要支付薪资,从项目角度看,这个学习成本还是没有绕过去,更糟糕的是开发者在没有极大利益为前提下是不希望去学习的,道理很简单,因为学了只能在一处使用,价值不大,对职业前景帮助不大,这是对个人而言的,对公司而言,形成对极大代价培养的员工的高度依赖,这成本很高,且风险很大,规模和人才等都会出现死穴,然而使用drupal这些问题会得到极大改善,她成熟、标准、已经做了很多、强大灵活、人才储备多,便开始显现其价值;伟大有影响力的项目都需要众多协作者,就像几个人不可能造出大飞机、航母,如果有缔造大项目的雄心那么应该挑战合理的困难,先爬到巨人的肩上,借势而为。
最后我们聊聊趋势,这里想告诉drupaler一个好消息:欧盟决定资助十四款开源软件的安全审计,CMS系统仅drupal入列,且这十四款软件中drupal预算排名第二,这传达了一种信心,将推动drupal发展,提升安全性,对于在drupal上投入精力的小伙伴前途又光明了一分,详见:
https://www.zdnet.com/article/eu-to-fund-bug-bounty-programs-for-14-open-source-projects-starting-january-2019/?from=timeline
回顾计算机历史,会发现很多东西一直在细分,最开始设计的计算机是一体的,后来细分为硬件和软件,软件又细分为操作系统和应用软件,然后继续细分,这个过程中每一次细分,在那一个点只会保留几个出类拔萃的代表供全部人使用,CPU架构就那几种,操作系统就那几种,WEB服务器就那几种,编程语言就那几种,您是否相信CMS也会就那几种?然后某领域应用的模块也会就那几种?云客是趋向于相信的,因为有个底层原因:在相同的平台上才能保证协作接口的一致性,只有一致才能进行大规模协作,才能提高文明的总规模。这是每一次细分只能保留少数几个的根本原因,如果保留了很多,那么他们一定有相同的标准接口,比如浏览器、播放器等, CMS出现标准接口可能为时尚早,因此drupal应能成为被保留下来的那少数几种之一。
大自然好像一直在要求生命去做能量最大化的事情,不断扩大规模,关于此吴军老师的科技史纲六十讲里面有很多很具说服力的论述,如果您相信这一点,就会知道一致性多么重要,就会趋向于相信drupal会成为web系统中的linux,既然如此,提前去投入可能是不错的选择。
2019年1月24日 云客 于中国深圳
我是云客,【云游天下,做客四方】,联系方式见主页,欢迎转载,但须注明出处