各位《云客drupal8源码分析》系列的读者:
本系列分享希望帮助大家理解drupal8底层原理,并缩短学习时间,但部分重点内容在博客仅发布前言和目录,这是因为:drupal在国外如此流行但在国内却很小众,一个重要原因应该是中文资料的缺乏,我们是否能够找到一种方式来激发高质量的中文资料出产?此时同是积极推动者的晴空提出能否用付费阅读的方式来鼓励更多教程作者?并建立了一个付费阅读drupal资料的网站,学习者仅需支付很少费用即可查询到最用心的知识注解,避免大量查找资料,可以节省大量学习时间是划算的,作者也能分配精力去开发高质量内容而得到回报,drupal是精品中的精品,超过一半的世界五百强用她做网站,难能可贵,国内普及初期需要一个良好的土壤,普及开后免费内容会越来越多,为了支持国内发展,云客决定将重点内容放在晴空的这个网站上收费阅读,读者只需要微信支付很少费用即可,云客在这里承诺该系列得到的所有订阅费用全部捐献给drupal深圳社区,用以组织活动等等,发展社区需要大家贡献力量,该网站于2017年3月29日上线,请移步:http://nowicode.com/ 阅读本篇完整版
我是云客,【云游天下,做客四方】,联系方式见主页,欢迎转载,但须注明出处
****************************************以下为前言和目录,请移步:http://nowicode.com/ 阅读本篇完整版*******************************
一个实体类型创建后,在她的生命周期中,她和她的字段定义可能出现更新、删除操作以满足业务需求,而不是一旦创建就不能被改变,所以要求她的储存Schema是动态的,在发生这样的事情时需要作出反应,典型的就是数据库schema(可简单理解为数据库表结构)的改变和数据的迁移或删除,系统为此定义了以下接口:
动态可字段实体储存模式接口:
Drupal\Core\Entity\Schema\DynamicallyFieldableEntityStorageSchemaInterface
定义了上述各种情况下需要的方法:
实体类型的改动:
public function onEntityTypeCreate(EntityTypeInterface $entity_type);
public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original);
public function onEntityTypeDelete(EntityTypeInterface $entity_type);
public function requiresEntityStorageSchemaChanges(EntityTypeInterface $entity_type, EntityTypeInterface $original);
public function requiresEntityDataMigration(EntityTypeInterface $entity_type, EntityTypeInterface $original);
字段定义的改动:
public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition);
public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original);
public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition);
public function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original);
public function requiresFieldDataMigration(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original);
public function finalizePurge(FieldStorageDefinitionInterface $storage_definition);
我们在定义一个实体类型时需要定义这个类型的储存Schema处理器以执行数据库表的建立、修改、删除等操作,实现的处理器需要实现以上的接口,在实体类释文处理器中的“storage_schema”键上面指定,如果没有指定那么使用系统默认提供的储存schema处理器:
Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema
此处理器实现了所有的基本功能,通常我们会继承她去实现自己的储存schema处理器,在自己的实现中只需要做一些优化即可,直接使用她也是可以的,见节点实体储存schema处理器:
Drupal\node\NodeStorageSchema
在这个处理器中就进行了索引优化,让表具备更快的查询效率,是否进行优化需要根据自己的实体类型而定,比如添加索引后查询效率是会快一些但是储存效率将会变慢,如何衡量需要看数据量和业务场景。
储存schema处理器的实例化和调用通常在储存处理器中进行(见储存处理器的getStorageSchema方法),本主题讲解这个系统默认提供的储存schema处理器。
在继续前你需要先明白schema数组结构,它是数据库组件提供的操作接口,程序只需要面向schema数组即可,数据库组件会根据schema数组产生具体的sql语句去维护数据库schema,如果你对主键、索引、唯一约束、外键约束和字段列定义还不熟悉,请先看本系列的数据库schema主题,那是本主题的基础。
默认储存schema处理器:
类:Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema
我们按照以上接口提供的方法来梳理各情况下系统执行什么样的流程:
实体类型创建时:
方法:onEntityTypeCreate(EntityTypeInterface $entity_type)
在该方法中会根据实体类型情况去创建她的共享表(包含:基本表、版本表、数据表、版本数据表)、字段专用表,然后保存实体类型的schema数据和各字段的schema数据,这些数据保存在键值系统的“entity.storage_schema.sql”集里面(关于键值系统见本系列键值储存主题)。在该方法中我们可以看到系统是如何通过字段储存定义去产生schema数组的。
实体类型更新时:
。。。
实体类型删除时:
。。。
判断是否需要储存Schema改变:
。。。
判断实体定义发生改变是否需要数据迁移:
。。。
字段定义创建时:
。。。
字段定义更新时:
。。。
字段储存定义删除时:
。。。
完成数据清理后:
。。。
注意以下两函数的区别:
getEntitySchema($entity_type, TRUE)
。。。
getEntitySchemaData(ContentEntityTypeInterface $entity_type, array $schema)
。。。
储存Schema的优化:
。。。
针对某个字段列进行优化可以重写以下方法:
。。。
模式标识符名SchemaIdentifierName:
。。。
补充:
1、实体类型的id键和版本键如果为int类型会被自动转换为serial类型(自增类型),并被设置为不能为NULL,默认值被忽略
2、实体id键、版本id键、bundle键如果是字符串类型,那么被限制为128字符,从专用表的schema中可以看出来
3、bug提示:在处理字段的外键约束时源字段名进行了属性名到数据库列名的转换,而目标字段名未做处理,见以下方法:
Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getFieldForeignKeys方法,因此在字段类型定义中,定义外键约束时,源字段使用属性名,而目标字段使用数据库中的列名,这也可不算bug,但有些不完美
4、目前共享表仅储存单值字段single-value fields,暂时不支持多值字段multi-valued fields(注意不是单/多属性字段)
节点实体默认Schema:
Array
(
[node] => Array
(
[description] => The base table for node entities.
[primary key] => Array
(
[0] => nid
)
[indexes] => Array
(
[node_field__type__target_id] => Array
(
[0] => type
)
)
[foreign keys] => Array
(
[node__revision] => Array
(
[table] => node_revision
[columns] => Array
(
[vid] => vid
)
)
)
[unique keys] => Array
(
[node__vid] => Array
(
[0] => vid
)
[node_field__uuid__value] => Array
(
[0] => uuid
)
)
[fields] => Array
(
[nid] => Array
(
[type] => serial
[unsigned] => 1
[size] => normal
[not null] => 1
)
[vid] => Array
(
[type] => int
[unsigned] => 1
[size] => normal
[not null] =>
)
[type] => Array
(
[description] => The ID of the target entity.
[type] => varchar_ascii
[length] => 32
[not null] => 1
)
[uuid] => Array
(
[type] => varchar_ascii
[length] => 128
[binary] =>
[not null] => 1
)
[langcode] => Array
。。。