33. 实体Entity概述
第一次在软件开发领域听说“实体Entity”是不是有点蒙圈不知道在说什么呢?我们经常有听过实体经济,但软件里面实体是个什么鬼?这个名词怎么来的?和那有什么关系么?drupal里面实体好像还很重要,甚至有人说drupal里面一切皆是实体;这个词在感觉上很不直观,有些读者觉得drupal难学,可能就是体现在这些地方吧,entity的英文翻译为:实体、本质、存在、物体,其实在不同的领域对实体有不同的定义,比如我们可以说由钢铁等组成的机器人身体是那个机器人的实体,当然实体不一定非得要是摸的着看得到的物质,如果你看过电影《黑客帝国》,里面的虚拟人物也可以看做是数字世界中的一个个实体,从这个角度看实体就是一种存在,在汉语中实体有一些解释,援引如下:
实体(Entity):客观存在并可相互区别的事物称之为实体。
指不仅可触知的而且是有形的东西
实际存在的物体,物质
可以搜索一下关于实体这个词的更多解释,在软件中实体以“实体(Entity):客观存在并可相互区别的事物称之为实体”做解释是比较合适的,有了这些印象我们来看一看drupal中的实体是什么意思。
让我们从一个例子开始:
假设现在让我们不使用任何框架,直接用php去开发一个小小的新闻系统,我们要做些什么呢?
首先写一个脚本,用来产生一个录入新闻的表单页面,接着写一个脚本来处理表单提交后的数据库保存工作,再写一个脚本来根据url中提交的id来显示新闻,如果需要修改更新新闻,那么还需要一个脚本读取被保存的新闻,然后将数据填到提交表单里面,再将表单发送给浏览器,用户修改后提交到录入脚本,要浏览新闻得写一个列表脚本,免不了要删除不适合的新闻还需要删除脚本,现在就开发出了一个最简单的新闻系统,可能许多小伙伴初学php时有过这样的经历吧。
初版的新闻系统形成后,很快我们发现了不足,我们看到产生了录入、保存、更新、显示、列表、删除6个脚本,太分散了!现在我们改用面向对象编程,定义一个新闻类:news,里面具备6个方法分别处理那6个操作,现在6个脚本变为一个类脚本了,用以访问的url中加上方法名作为参数,页面模板也可以分离出来保存到模板文件夹中,现在这个新闻系统变的更加紧凑了,也更加合理了!
现在看起来我们的新闻系统已经很好了,但本质上它只是以面向对象编程方式包装着一个面向过程的思路而已,让我们真正以面向对象的方式,发挥oop的精髓,将一切都看做对象,接着来看这个例子,进步无止境,来把它做成一个简单的cms系统,有了这个想法后发现新闻系统只不过是cms中的一个子对象而已,想到了一种新的方法来实现新闻系统,采用完全不一样的方法,依然用一个类来实现新闻系统,发挥OOP的精髓,整个新闻系统用一个对象表示,只不过它是在cms中而已,预想它的使用方法是这样的:(见辅助内容区)
除了这些,还可以很灵活的加许多功能到这个新闻类上面去,比如保存新闻的时候可以执行数据验证,防止标题为空等等,只需要这样:
$isOk=$new->validate();
还可以做权限检查,这样来:
$isOk=$new->access($operation, $account = NULL );
参数$operation为操作标识,如“create”、“delete”等等,参数$account为账户对象
甚至要得到某篇新闻的url可以这样:
$url=$new->toUrl();
$new=new news(); //初始化新闻系统,新闻系统的所有逻辑都在这个类里面
$new->create($arr); //创建一篇新闻,参数是一个数组,作为可选值,键名为新闻的字段名、键值为字段值,如:['title' => '这是新闻标题内容', 'body' => "这个是新闻内容"]
$new->title="新的标题"; //创建后还能修改或赋值所有的新闻属性
$new->save();//创建好了,这个对象自己负责保存新闻到数据库,cms程序简单的调用一个方法即可
$new->load($id); //简单的调用一个方法就可以将以前保存的某篇新闻初始化到一新闻对象中
$new->delete($id); //简单的调用一个方法就可以删除某篇新闻
echo $html=$new->list(); //显示新闻列表
echo $html=$new->toString (); //显示新闻
这个实现新闻系统的想法实在是太棒了,在cms中和其他组件的接口非常简单,而且操作灵活!
此时忍不住想思考这里的$new对象现在成了什么呢?它包含着新闻的数据,还有对新闻的各类操作,一切都变得简单了。
说到这里你可能已经开悟了,它!就是实体!“客观存在并可相互区别的事物”!这个想法就是drupal中实体概念的最初来源,实际中drupal系统里的实体概念现在已经发展的更加成熟和全面。
带着这种开悟的喜悦,接着看看这个$new实体,我们发现在cms中不止是新闻可以这么办,其他的内容比如:文章、产品也可以这么办,只不过类型不同而已,这里就产生了一个概念:“实体类型”,前面提到的$new只是诸多类型中的一个而已,不同的类型可能名称不同(新闻、产品)、数据模型不同(标题、型号、价格等)、针对数据的验证不同等等,由此有必要设置一个实体类型对象来保存实体的这些相关信息,它和实体对象配合起来使用。
现在有了实体、实体类型的概念,他们为cms的架构带来了极大的灵活性,我们发现这些实体有个共同特点:都是来保存cms中的内容的,至少会有个标题,或者说是id,用来索引这些内容,我们可以将这些共同点归纳起来,形成一个基础对象,或者叫基础实体,它只包含id这样的基础字段,和基础CRUD方法,其他实体就不用重复实现这些内容,只需要继承这个基础实体,添加自己额外的特有字段和方法就可以了,这样一来就产生了一个新概念:基础实体是一种实体类型,继承它的实体是子类型,在drupal中节点Node就是基础实体,只包含内容的id等,不同的内容类型,如文章、新闻等等就是子类型,在drupal的世界里面子类型又叫父类型的bundles,硬性翻译为捆、束,在理解上它是同类事物的细分,当然有些实体是没必要有子类型的,他们就没有bundles。
如果没有上述过程、来历的介绍,理解实体是很困难的,它们较为抽象,云客有些感叹:国内的许多cms系统对内容的抽象只是到了栏目一级,不同栏目有不同数据模型,没有想过它们本质上是相同的,没有更进一步的抽象出实体这个概念来,还有待发展吧,也许也跟php从面向过程到面向对象转变的过程有关,这种转变在国内的体现还很慢,国外程序员重于追求最合理最好,而国内程序员太迁就于适应环境。
在drupal中出现实体概念后很快就发现本质上实体是一种存放信息的容器,同时带了对信息的各种操作,既然如此,实体就不止是系统中管理的内容了,还可以用来代表配置,在drupal8中目前已经形成了如下的实体,以层级关系列出:(见辅助内容区)
在drupal中用户也可以自定义实体,实体这个概念的发展还在继续,它是伴随着抽象层级的提升而提升的,我们需要注意它的解释“客观存在并可相互区别的事物”,说不定很久以后drupal已不再是一个cms系统,而是一个数字世界,实体就是那个世界中的一位虚拟的有意识的人,未来未成定数一切尚未可知。
回归正题,我们来看一下官方文档对实体的解释:
实体变体:
内容:
内容实体类型:
节点:
文章(节点的Bundles)
基本页(节点的Bundles)
分类:
词汇表A(分类的Bundles)
词汇表B(分类的Bundles)
词汇表...
内容块:
块内容A(块的Bundles)
块内容B(块的Bundles)
块内容... (块的Bundles)
配置:
配置实体类型:
视图(没有bundles)
菜单(没有bundles)
角色(没有bundles)
实体Entities:
从程序角度来讲,在drupal8中实体是一个持久化储存内容或配置的对象,比如将数据储存到数据库就是一种持久化储存,这个对象处理数据的加载、储存、修改、删除、验证等,从用户角度来讲,实体是被管理数据的高级抽象,相当于一个大的数据结构,但它不只是包括数据,还包括处理数据的方法,如一个对象代表一篇新闻,它包含新闻标题、副标题、内容、日期等等内容,并且这个对象还提供方法处理这些数据的储存、加载、编辑、删除等等,那么这个对象就是这篇新闻的实体对象,它就是一个实体。
在drupal中实体要处理的事情很多,比如实现可修订版本功能(同一内容存在多个版本),可翻译功能(有些字段是语言相关的)、各类操作表单的处理、各种列表和内容的显示方式、访问权限控制、储存问题等等,内部逻辑会比较复杂,因此它是系统中一个很大的子系统,建立在许多基础组件之上,本系列会分多个主题来介绍实体各个部分,要准备学习实体需要具备哪些前提知识呢:
钩子和插件:它们将模块个核心连为一个整体,实体系统中大量使用
字段api:解决内容实体的数据库储存、显示、表单等相关问题
类型化数据api:实体也可以看做是复合数据类型,构建在类型化数据组件之上,实现方便操作
权限控制:用于实体的访问检查
语言和翻译:实体的多语言功能必不可少
以上这些主要内容云客源码分析系列大多已经发布了,如果一直跟随而来将不会陌生,请看相关文章,还未发布的或只发布了上篇的内容,接下来会发布,顺序上尽可能做到顺应学习过程,一步一步的理解学习。
点燃好奇心,列出一个列子,先一窥实体的魅力吧:
在自定义模块的控制器中执行以下代码:
$nodeStorage = \Drupal::entityTypeManager()->getStorage("node"); //获取节点实体储存处理器
$nodeEntity = $nodeStorage->create(['type' => 'article', 'title' => '实体测试20180914', 'body' => "我的实体内容"]); //创建一篇文章(创建一个实体)
$nodeEntity->save(); //保存实体
然后到管理页面后台的内容里面,看一看是不是就添加了一篇文章呢
$entity = \Drupal::entityTypeManager()->getStorage("node")->load(1);
echo $entity->label();
加载一个节点,显示它的标题