43. 实体entity(一):实体基类
注:本系列先前已发布过两篇关于实体基础性介绍的主题:《实体概述》和《实体类型管理器、实体类型、实体》,请先查阅,从本主题开始将深入到具体的代码细节,并以序列编号有关实体的主题,该序列将指导学习的顺序。
纵观实体概念有一些要点:
实体对象本身:保存着数据和一些常用方法
实体类型对象:保存实体的元数据,来自于实体类定义中的释文头部
实体处理器:一系列针对实体处理的一些操作,如储存、访问控制、列表构建、表单构建、路由提供、视图数据、视图构建等等,这些处理器在释文元数据中指定,每一种类型的实体对应的处理器可能是不一样的,往往是特定的类
实体类型对象比较简单,主要是通过插件机制将定义在实体插件释文中的元数据注入到该对象中,便于操作,对必要的元数据如果没有定义的情况下设置默认值,在使用过程可以通过set方法对元信息进行更改,默认实现是:
Drupal\Core\Entity\ EntityType:(Drupal\Core\Entity\ EntityTypeInterface)
现在系统中有两大类实体:内容实体和配置实体,根据少量差异分别为他们实现了实体类型对象,他们都继承自以上的实体类型基类:
Drupal\Core\Entity\ContentEntityType(Drupal\Core\Entity\ContentEntityTypeInterface)
Drupal\Core\Config\Entity\ConfigEntityType(Drupal\Core\Config\Entity\ConfigEntityTypeInterface)
要理解实体先从实体基类开始:
Drupal\Core\Entity\Entity: (Drupal\Core\Entity\EntityInterface)
实体接口归纳提炼了实体的基本方法,该类是系统定义的一个实体抽象基类,内容实体基类:
Drupal\Core\Entity\ContentEntityBase (Drupal\Core\Entity\ContentEntityInterface)
和配置实体基类
Drupal\Core\Config\Entity\ConfigEntityBase (Drupal\Core\Config\Entity\ConfigEntityInterface)
均继承它,后两者又被各种具体的实体继承,就这样一层一层的实现了实体系统,因此学习实体先从该实体基类开始,这也是本篇的主题。
Entity类是系统中所有实体的基类,里面定义了实体的通用基本方法,在所有实体对象上均可调用,这是一个对实体概念精炼抽象后的精髓部分,需要仔细理解每一个方法,在本系列的实体概述主题中举了新闻系统的列子来说明实体,可以看出实体对象本质上就是一个包含某类型信息的实际数据并实现了作用在这些数据上的方法的对象,因此Entity基类的构造函数有两个参数:
public function __construct(array $values, $entity_type)
第一个参数$values表示要传入的信息数据,是一个关联数组,实体对象用它的键名作为实体属性名,第二个参数$entity_type是一个字符串标识符,表示传入的是哪一类型的信息,也就是实体类型id,实体对象依据该信息从实体类型管理器中获取包含实体元数据的实体类型对象,从而得知怎么去操作传入的数据。
以下是实体基类中的要点说明,简单方法不做说明。
判断实体是否为新:
默认的没有实体id或设置了强制为新则认为实体是新建的,尚未保存,该默认实现相对于内容实体是有效的,但不适合于配置实体,因此配置实体基类对他进行了重写,强制为新是为了配置的导入导出等特殊需求
实体链接:
实体需要列表链接、查看链接、各种表单链接等等,链接url有一定格式,对应着自己的路由对象,在元数据的links项中定义了链接模板,模板中可以有以下占位符,用大括号包裹,在产生链接时会被替换为具体实体的具体值:
实体类型名:实体id
类型定义中bundle_entity_type的值或者在类型定义里entity_keys中的Bundle的值:实体bundle()的值
实体类型名+'_revision':版本id,也就是实体方法getRevisionId()的值
链接操作中会涉及Url和Link对象,对它们的介绍在另外的主题中进行
访问控制:
这分为两大类:是否有权创建、创建后可以进行什么操作
缓存标签:
缓存标签可以通知到缓存系统实体是否已经变化,以便及时让缓存失效,每个实体有自身的通用标签,标签名为:实体类型id+“:”+实体id
可以给实体设置cacheTags属性,以指定额外的缓存标签,这和实体自身的通用标签一起构成实体标签,也就是实体:getCacheTags()方法的值
实体的列表标签在实体释文中的list_cache_tags项指定,如果没有设置,将是默认的:实体类型id+ '_list',这可以通过实体类型类的getListCacheTags()方法得到
保存实体时失效以下缓存标签:
实体列表(代表着用户看到的列表页)、4xx-response(实体改变可能导致4xx状态码失效)、自身通用标签(通知用到该实体的地方失效缓存)
常用静态方法:
load($id)、loadMultiple(array $ids = NULL)、create(array $values = [])是静态方法,这意味着可以在类实例化之前调用,这些操作需要知道实体类型id,但此时还不知道,那么如何得知?可以通过类名,这用到了容器中的以下服务:
容器id:“entity_type.repository”
类:Drupal\Core\Entity\EntityTypeRepository
该服务提供两个辅助方法:
getEntityTypeFromClass($class_name):通过全限定类名来得到实体类型id
getEntityTypeLabels($group = FALSE):得到所有实体类型的描述标签,参数指定是否按照实体释文中的group_label分组返回,如果实体释文中没有设置group_label,那么会使用释文对象中的值,系统中所有实体默认属于两个分组,分别为内容实体组和配置实体组,自定义的实体可以指定该标签产生自己的组,使用该方法示例如下:
$Labels= \Drupal::service("entity_type.repository")->getEntityTypeLabels(true);
foreach ($Labels as $k=>$v)
{
echo "分组: ".$k."\n";
foreach ($v as $a=>$b) {
echo " ".$a." : ".$b."\n";
}
}
配置依赖键和配置依赖名:
配置实体可以有依赖需求,可以依赖其他的内容实体或配置实体,在安装配置实体时只有所依赖的实体全部存在它才会被安装,因此在配置实体基类中定义了一个$dependencies属性,它是一个数组,里面保存着该配置需要的依赖,具体就是依赖名,它来自被依赖实体的getConfigDependencyName()方法,
在系统中有时候需要将依赖储存在数组里进行传递,需要一个数组键名,默认下,内容实体的依赖键为“content”,配置实体的依赖键为“config”,这并不用在前文提到的$dependencies属性中,以后会有体会
类型化数据:
实体可以通过类型化组件提供的功能转化为复合类型的类型化数据对象,这样可以进行许多方便的操作,比如数据验证等等,在了解本节前请先阅读本系列的类型化API主题,在基类中将一个实体转化为类型化数据的方法为:
getTypedData(),核心代码是:
$class = \Drupal::typedDataManager()->getDefinition('entity')['class'];
$this->typedData = $class::createFromEntity($this);
该方法在配置实体基类和内容实体基类中没有被覆写,大部分实体直接使用它,通过类型化数据管理器得到的实体插件重要定义如下:
class类型类:Drupal\Core\Entity\Plugin\DataType\EntityAdapter
definition_class定义类:Drupal\Core\Entity\TypedData\EntityDataDefinition
list_class列表类:Drupal\Core\TypedData\Plugin\DataType\ItemList
list_definition_class列表定义类:Drupal\Core\TypedData\ListDataDefinition
deriver派生类:Drupal\Core\Entity\Plugin\DataType\Deriver\EntityDeriver
可以看出是:
Drupal\Core\Entity\Plugin\DataType\EntityAdapter::createFromEntity($this);
创建了实体的类型化数据对象
目前实体的类型化暂不支持配置实体,见:https://www.drupal.org/node/1818574
因为类型化会涉及到字段api、实体约束、插件派生等内容,将在介绍完涉及的内容后专门开一个主题详细讲解
补充说明:
- 注意实体基类中没有提供get和set方法,他们在内容实体基类和配置实体基类中实现