45. 实体entity(三)配置实体储存处理器
配置实体储存处理器
精炼一点看实体由两大部分构成:数据和动作,实现各种动作的类我们称之为实体处理器,有很多,如储存处理器、表单构建处理器、访问控制处理器、翻译处理器等等等等,这些类在实体释文的handlers属性中指定,系统为实体处理器提供了一个通用接口:
Drupal\Core\Entity\EntityHandlerInterface
但并不要求所有的处理器一定要实现它,如果处理器实现了它,实例化时会将服务容器注入,注入是在实体类型管理器的createHandlerInstance方法中实现的。
系统为所有的实体处理器提供了一个抽象基类:
Drupal\Core\Entity\EntityHandlerBase
功能主要为获取和设置模块处理器(用来执行钩子),但因为有多种方式得到模块处理器,所以该基类在8.0版本中被弃用了,将在9.0版本时移除,请勿再使用
本篇讲解非常重要的实体储存处理器,大多数实体需要它。
储存处理器基类:
为了实现实体的持久化存储,需要储存处理器,它提供保存、创建、删除等等CRUD功能,系统提供了一个储存处理器抽象基类:
Drupal\Core\Entity\EntityStorageBase
实现了接口:
Drupal\Core\Entity\EntityStorageInterface
Drupal\Core\Entity\EntityHandlerInterface
针对现阶段的两大实体类型,实体储存处理器有以下默认实现:
内容实体储存处理器:\Drupal\Core\Entity\Sql\SqlContentEntityStorage
配置实体储存处理器:\Drupal\Core\Config\Entity\ConfigEntityStorage
下面看一看实体储存处理器抽象基类都做了些什么
配置实体和内容实体的储存细节是不一样的,所以她不涉及具体的储存方法,是一个抽象基类,实现了以下工作:
静态缓存:
如果在实体释文中指定了static_cache = TRUE说明实体实例化后可以缓存在储存处理器对象中,以供多次使用,如果不设置或设置为false那么说明实体是不可静态缓存的,从这里可知如果是可缓存的实体,在释文中我们需要设置该值,此设置影响运行性能
执行各类钩子:
涉及实体的钩子根据级别分为两大类,在一个方法中统一执行:
具体实体级别的钩子,钩子名:$entityTypeId . '_' . $hook,用于模块针对某类型实体的处理
实体概念级别的钩子,钩子名:'entity_' . $hook,用于针对所有实体,比如统计功能等等
具体实体级别的钩子优先执行,执行完后实体概念级别的钩子再执行,所有钩子被传入实体对象本身,有如下钩子:
创建钩子:
$hook='create',实体对象创建后,保存前执行,如果还需要进一步细化执行创建前和创建后的钩子,需要在实体类中的preCreate和postCreate方法里实现派发,他们在'create'钩子之前执行。
(在模块中实现'create'钩子的函数名为:模块名.’_entity_create’ 和 ‘模块名_’.$entityTypeId . '_create’ ,下同)
加载后钩子:
函数名为:$module . '_entity_load'和$module . '_' . $this->entityTypeId . '_load',执行顺序实体级别优先
删除前钩子:
$hook='predelete',
删除后钩子:
$hook='delete',
保存前钩子:
$hook='presave'
更新钩子:
$hook='update' ,用于已经存在的实体的更新保存后操作
插入钩子:
$hook='insert',用于新建实体的保存操作
以上就是这一层基类的主要功能。
配置实体储存概述:
大多数实体是需要持久化储存的,当实例化一个实体对象时,需要向其构造函数提供一个数组参数,那是实体把持的数据内容,对于内容实体而言,该数组通常是数据库中的一行记录,或组合查询的一行结果,而配置实体则不同,默认情况下配置实体是建立在简单配置对象之上的(在本系列前面的主题中已经详细描述了简单配置对象,请先查阅),系统使用简单配置对象子系统为配置实体提供储存功能,为配置实体构造函数提供的数组来自于简单配置对象中包含的数组,也可以说每个配置实体包裹着一个简单配置对象,因此配置实体的数据和简单配置对象一样储存在数据库的config表中,在储存上做到了统一;配置实体同样有与之对应的配置对象名,命名规则和简单配置对象是一样的,使用是统一的,见下文;简单配置对象有覆写概念,因此配置实体也有;在配置实体储存处理器基类中实现了上述各功能,见下文。
配置实体的配置对象名:
她由两部分组成:配置前缀和配置id,由点号分隔
配置前缀也由点号分隔的多个部分构成,第一部分表示该配置对象的所有者,也就是模块扩展等,随后部分是由该所有者模块指定的子名字空间(类似子目录),该随后部分可以在实体释文的config_prefix键中指定,如无指定那么使用实体类型id(注意不是实体id)
配置id是由模块指定的配置识别标志,也就是配置实体的实体id
实体的配置对象名和简单配置系统中的配置对象名是统一的,规则一样。
在配置实体储存处理器中声明了配置id的长度为小于等于166字符,因为在配置导出时是导出到以配置对象名命名的文件中,而大多数文件系统限制文件名长度为255字符,减去5字符的扩展名,还剩下250字符,配置前缀被限制小于等于83字符,还剩下250-83=167字符,要一个点号做分隔符,所以剩下166字符。
配置实体储存处理器基类:
这是所有配置实体储存处理器的基类,她继承自通用的储存处理器基类,在其中实现了主要的配置实体储存逻辑,类如下:
Drupal\Core\Config\Entity\ConfigEntityStorage
实现了如下接口:
Drupal\Core\Config\Entity\ConfigEntityStorageInterface
和可导入实体储存接口:
Drupal\Core\Config\Entity\ImportableEntityStorageInterface
该接口用于处理配置导入,在不同储存间同步配置时,储存需要处理同步性,在本篇中暂不讲解相关内容,后续将有专门主题讲解配置安装、导入、导出、同步等内容。
以下讲解一些要点。
overrideFree属性:
该基类设置了overrideFree属性,布尔值,默认为false,是免覆写设置,true表示需要免覆写,表明实体包裹的是未经覆写的从数据库查询出来的原始值,注意并不是配置对象中的data属性值,后者可以通过配置对象的set方法设置;false表示需要进行覆写,实体包裹的是经过覆写的值。
静态缓存:
重新声明了$entities属性,它的结构和父类不一样,两层嵌套数组结构,是配置实体特有的,以实体id为第一层嵌套键名进行静态缓存,同一个id的实体可能有多个缓存,区别在于是否进行配置覆写,这取决于overrideFree值的设置,需要进行配置覆写的情况下也可能有不同覆写设置,所以需要第二层嵌套数组,其键名为:
\Drupal\Core\Config\ConfigFactoryInterface::getCacheKeys()
返回数组的冒号链接字符串,用此方式可以识别覆写状态,覆写改变被缓存的配置对象将改变,如果没有覆写的情况下键名为空字符串,这是一个php底层技巧,比较罕见:允许空字符串作为数组键名,注意是空字符串并不是NULL
缓存元数据:
实体需要继承配置对象的缓存元数据,但不是简单的继承,需要去除自引用的缓存标签self-referring cache tag(代表配置对象自己的缓存标签),原因如下:
当重命名或复制配置实体时,配置对象的自引用缓存标签还在,这可能引起错误
有些配置实体不使用内含配置对象提供的缓存标签,这带来性能和缓存性上的收益,比如日期格式配置实体,每个配置实体都有一个专门的缓存标签有时是没有意义的
从缓存系统层面来讲,更少的缓存标签对于提高性能来说是比较好的
常量定义:
配置实体保存操作进行后会返回以下常量:SAVED_NEW表示新建成功、SAVED_UPDATED表示更新成功,这些常量在\core\includes\common.inc中声明
补充说明:
虽然程序上可以用简单配置对象去操作修改配置实体的内容,但这样做将不会执行实体相关钩子,所以请避免或注意。
如何在钩子中影响配置实体的CRUD操作?执行与储存相关的钩子是在实体储存基类中进行的,她会将目标实体放置在数组中传递给钩子函数并执行,但并不关心钩子函数返回什么内容,所以钩子的返回值是没有意义的,不会有任何影响,在php中对象是以引用方式传递的,所以钩子可以和目标实体对象直接交互,可以改变数据但无法打断CRUD流程,也就是说钩子中不能直接实现禁止保存、禁止删除这样的操作,如果需要这样的操作,需要从其他方面入手,如继承实体并重写,或在不同时机的钩子中对前流程进行更改,比如要阻止保存操作,可以在保存后钩子中进行删除操作,变相实现禁止保存,关于此可以了解具体配置实体的具体实现;可能你想到了修改配置实体id或在钩子中抛出异常的方式来进行这样的操作,但那是不优雅不合理的。