Elementor Global Classes 数据模型解析:CPT 持久化、Kit Meta 与仓库 API 实战指南
【免费下载链接】elementorThe most advanced frontend drag & drop page builder. Create high-end, pixel perfect websites at record speeds. Any theme, any page, any design.项目地址: https://gitcode.com/GitHub_Trending/el/elementor
导读
Global Classes(全局类)是 Elementor 4.0 原子构建体系中用于承载"可复用视觉样式"的 Kit 级数据模型:每个全局类以一条 WordPress 自定义文章类型(CPT)记录持久化,其顺序、标签映射与 ID 索引则存放在 Site Kit 的文章元数据(post meta)中。本文以 docs/atomic-builder/global-classes/data-model.md 为核心主线,逐层拆解其存储布局(CPT + kit meta)、{ items, order }内存形态、label与内部g-*ID 的区别、数据不变量(含 1000 条上限与顺序规范化)、sync_to_v3同步机制、Repository/Parser 公开 API,并结合modules/global-classes/下的源码实现,帮助你理解 Kit 导入导出(global-classes.json)、调试 RESTPUT载荷与 MCPmanage-classes、追踪预览态与发布态差异,以及理清每个 Kit 的隔离边界。
数据模型总览:两层存储 + 一个内存形态
Global Classes 的数据并非单一存储,而是"CPT 帖子 + Kit meta + 内存值对象"三层配合:
| 层次 | 载体 | 职责 |
|---|---|---|
| CPT 帖子 | e_global_class(每类一条) | 保存标签(label)、内部 ID、发布态/草稿态 variants |
| Kit post meta | 活跃 Site Kit 上的 meta | 顺序、标签映射、id → 帖子ID查找表、v3 同步标记 |
| 内存值对象 | Global_Classes | 统一对外暴露{ items, order }形态 |
内存中的完整形态如下(来自>{ "items": { "g-abc123": { "id": "g-abc123", "label": "wc26-gold", "type": "class", "variants": [ { "meta": { "breakpoint": "desktop", "state": null }, "props": { "color": { "$$type": "color", "value": "#D4AF37" } } } ] } }, "order": ["g-abc123", "g-def456"] }
items以内部 ID 为键,order是 ID 的有序数组。两条关键约定需要牢记:
- 面向作者(author-facing)的上下文一律用 label 引用类,例如 HTML 的
class属性、MCP 的classes映射; - 内部 ID(
g-*)是稳定存储键,出现在元素 JSON 的settings.classes.value、REST 更新/删除以及 Kit meta 中,不应对作者暴露。
从实现上看,Global_Classes_Repository::all()会先读 Kit 的 order meta,再按 ID 批量拉取 CPT,最后组装成Global_Classes::make( $items, $order )值对象,并在组装时调用Global_Classes_Parser::sanitize_order()兜底修复顺序(见 global-classes-repository.php 与 global-classes-parser.php)。
何时需要理解这份数据模型
- 理解 Kit 导出结构:导入/导出产生的
global-classes.json正是{ items, order }的序列化形态,模块内的 import-export/export-runner.php 与 import-export/import-runner.php 直接操作该结构; - 调试 REST
PUT载荷或 MCPmanage-classes:PUT elementor/v1/global-classes的参数(items、order、changes)与本数据模型一一对应,理解校验逻辑才能定位 400 报错; - 追踪预览态与发布态:草稿与发布分别使用独立的 meta 键,阅读数据模型可解释"编辑器里改了没生效/发布了才生效"的现象;
- 推理每个 Kit 的隔离:所有类帖子与 meta 都归属活跃 Kit,不同 Kit 之间互不可见。
CPT 持久化:e_global_class
每个全局类对应一条e_global_class类型的帖子。该 CPT 在 global-class-post-type.php 中注册:public => false(不对外公开、无前端 URL),仅supports => ['title'],即只用标题字段。
字段与存储位置的映射如下(原文档表格):
| 字段 | 存储位置 |
|---|---|
| Label(标签) | post_title |
| Internal id(内部 ID) | _elementor_global_class_id |
| Published variants(发布态变体) | _elementor_global_class_data |
| Draft variants(草稿态变体) | _elementor_global_class_data_preview |
这些 meta 键在 global-class-post.php 中以常量定义:
const META_KEY_VERSION = '_elementor_version'; const META_KEY_ID = '_elementor_global_class_id'; const META_KEY_DATA = '_elementor_global_class_data'; const META_KEY_DATA_PREVIEW = '_elementor_global_class_data_preview'; const META_KEY_EDITED = '_elementor_global_class_edited';Global_Class_Post还额外维护两个辅助 meta:
_elementor_version:记录数据写入时的 Elementor 版本,供迁移编排器判断是否需要跑 prop-type 迁移;_elementor_global_class_edited:最近编辑时间戳(发布态写入时刷新),用于判断"类是否被编辑过"(was_edited()比较该时间与创建时间)。
Global_Class_Post::to_array()负责把帖子归一化为StyleDefinition形态:先读取上下文对应的 variants 数据,合并label,再经Global_Class_Data_Normalizer::normalize_style()输出{ id, label, type, variants }(见 utils/global-class-data-normalizer.php)。一个值得注意的实现细节:草稿态数据为空时,读取会回退到发布态数据(get_data()中empty( $data ) && is_preview()分支),因此一个从未被编辑过的类,在预览上下文下也能读到发布内容。
写入侧,Global_Class_Post::create()以wp_insert_post创建帖子,写入META_KEY_ID、META_KEY_DATA、META_KEY_VERSION,并在 Kit 的Global_Classes_Post_IDs映射中登记class_id → post_id。update_data()按当前上下文写入 variants meta;非预览写入时还会同步清除草稿 meta(delete_post_meta( ..., META_KEY_DATA_PREVIEW )),保证发布态总是权威的。
Kit meta:顺序、标签映射与 ID 索引
每个类的内容存在 CPT 上,但"类如何组织成 Kit"的信息——顺序、标签映射、ID 索引——全部存放在活跃 Site Kit 的 meta 中。完整清单如下(原文档表格):
| Meta 键 | 用途 |
|---|---|
_elementor_global_classes_order | 发布态类 ID 顺序 |
_elementor_global_classes_order_preview | 草稿态顺序 |
_elementor_global_classes_labels | 发布态id → label映射 |
_elementor_global_classes_labels_preview | 草稿态标签映射 |
_elementor_global_classes_post_ids | 内部 ID → CPT 帖子 ID |
_elementor_global_classes_sync_to_v3 | 已同步到 v3 全局字体的类 ID |
对应源码常量分别位于 global-classes-order.php(_elementor_global_classes_order/_elementor_global_classes_order_preview)、global-classes-labels.php(_elementor_global_classes_labels/_elementor_global_classes_labels_preview)与 global-classes-post-ids.php(_elementor_global_classes_post_ids)。
各存储组件都实现相同的"上下文切换"模式,通过Has_Preview_Contexttrait 在frontend(发布)与preview(草稿)之间切换,并具备以下行为:
- 顺序:
Global_Classes_Order把 ID 数组以['order' => [...]]载荷写入 Kit meta;预览态读不到内容时自动回退到发布态(read_kit_meta_payload()中的回退逻辑); - 标签:
Global_Classes_Labels维护扁平映射,get_ordered_labels()会按 order 输出有序的id → label,预览态缺失的标签从发布态映射补齐; - ID 索引:
Global_Classes_Post_IDs保存class_id → post_id映射,读取时会校验帖子仍存在,若已被删除则自动从映射中移除(get_post_id()的清理逻辑);同时它挂在deleted_post钩子上,任何e_global_class帖子被删除后都会在所有 Kit 中清除对应条目。
仓库上下文(Context)
Repository 通过两个常量区分读写上下文:
const CONTEXT_FRONTEND = 'frontend'; const CONTEXT_PREVIEW = 'preview';REST 端点、MCP 调用以及 JSapiClient.all( context )都接受这两个取值。set_preview()切换上下文时会清空内存缓存(on_preview_change()将$cache置空),避免脏读。
Label 与内部 ID:两个概念,一个对应关系
这是全局类数据模型中最容易混淆的一对概念:
| 概念 | 出现位置 | 说明 |
|---|---|---|
| Label | HTMLclass属性、MCPclasses映射 | 公开的 CSS 类名,例如wc26-gold |
内部 ID(g-*) | 元素settings.classes.value、REST 更新/删除 | 稳定存储键,随导入导出保持不变 |
元素侧通过Classes_Prop_Type(modules/atomic-widgets/prop-types/classes-prop-type.php)保存有序的内部 ID 引用数组;渲染时由样式系统把 ID 解析为 label 输出为最终 CSS 类名。
重复 label 的自动改名是数据完整性的一道防线,实现在Global_Classes_Labels::generate_unique_label():
- 冲突标签会被加上
DUP_前缀并追加递增数字(如DUP_wc26-gold1); - 最终长度上限50 字符(
$max_length = 50),超长时先截断原始部分再拼接计数器; - 已有
DUP_前缀的标签再次冲突时,会在前缀基础上继续递增计数器,保证始终唯一。
在 REST 层,Global_Classes_REST_API::put()会先调用Global_Classes_Parser::check_for_duplicate_labels()检查新增项是否与存量标签冲突;若冲突,服务端自动改名并返回{ "code": "DUPLICATED_LABEL", "modifiedLabels": { "<item_id>": { "original": "...", "modified": "DUP_..." } } },客户端可根据modifiedLabels回显修正后的名称(见 global-classes-rest-api.php)。
数据不变量(Invariants)
模型层保证以下约束不被破坏:
order是级联与 UI 的权威排序:层叠(cascade)优先级与界面展示都依据该数组,后出现的条目在样式冲突时胜出;items的键必须与order匹配:解析器通过sanitize_order()兜底——过滤非字符串项、去重、只保留 items 中真实存在的 ID,并把缺失的 ID 按字符串排序追加到末尾;parse_order()则严格校验order与最终 item ID 集合完全一致,缺失项报missing、多余项报excess;- 每个 Kit 最多 1000 个类:上限常量定义在
Global_Classes_REST_API::MAX_ITEMS = 1000。PUT写入时计算当前总数 - 删除数 + 新增数,超限返回 400,错误码global_classes_limit_exceeded,并在 meta 中携带current_count与max_allowed; - 新 Kit 会克隆上一个 Kit 的类帖子:
Global_Class_Post::clone_to_kit()/clone_to_other_kit()会复制源帖子的_elementor_global_class_id、发布态与草稿态数据、版本与编辑时间戳,并在目标 Kit 的Global_Classes_Post_IDs中重新登记,实现跨 Kit 的样式继承。
解析器的完整校验链路
Global_Classes_Parser(global-classes-parser.php)是载荷校验的核心,完整流程如下:
parse( $data ):要求$data同时包含items与order两个键,否则返回missing错误;随后依次执行parse_items与parse_order,两者都通过才返回{ items, order }的规范化结果;parse_items( $items ):对每个 item 调用Style_Parser(基于Style_Schema校验 variants 与 props,见 docs/atomic-builder/fundamentals/style-schema.md);同时强制item 的键必须与 item 内id字段一致,不一致时报mismatching_value并拒绝该条;parse_order( $order, $final_item_ids ):去重、仅保留字符串后,对比期望 ID 集合,报告missing/excess。
RESTPUT的参数约束(changes对象)也在这里体现:
{ "changes": { "added": ["g-abc123"], "deleted": [], "modified": ["g-def456"], "order": true }, "items": { "g-abc123": { "id": "g-abc123", "label": "wc26-gold", "type": "class", "variants": [] } }, "order": ["g-abc123", "g-def456"] }其中changes的四个字段(added/deleted/modified为字符串数组,order为布尔值,表示顺序是否发生变化)在Global_Classes_Repository::apply_changes()中决定批处理动作。
sync_to_v3:可选的传统全局字体同步
sync_to_v3是类 item 上的一个可选布尔字段,表示该类的排版(typography)是否镜像同步到传统 v3 全局字体(Global Fonts):
- 值为
true时,类中的字体相关 props 会通过 modules/design-system-sync/ 模块同步到 v3 体系,保证旧特性与新原子样式库不脱节; - 该字段仅在编辑器 UI 中提供开关,MCP 不暴露此字段;
- 数据归一化时,
Global_Class_Data_Normalizer::normalize_style_fields()会把sync_to_v3强制转换为布尔值((bool) $item['sync_to_v3']); - 在
apply_changes()/put()的非预览分支中,变更会交给Global_Classes_Sync_Map::apply_changes( $touched_items, $to_delete )驱动实际同步。
Public API:Repository 与 Parser
数据模型对外暴露的 PHP API 集中在 Repository 与 Parser 两个类(原文档表格,签名以源码为准):
| 类 | 方法签名 | 用途 |
|---|---|---|
Global_Classes_Repository | public function all( bool $force = false ): Global_Classes | 读取全部类,返回{ items, order }值对象;带内存缓存,$force = true强制穿透 |
Global_Classes_Repository | public function get( string $class_id ): ?array | 按内部 ID 读取单个类(无则返回null) |
Global_Classes_Repository | public function get_by_ids( array $class_ids ): array | 按 ID 批量读取 |
Global_Classes_Repository | public function get_order(): array | 返回有序 ID 列表 |
Global_Classes_Repository | public function apply_changes( array $touched_items, array $changes, array $order ): void | 批量写入增量变更(增/删/改 + 顺序) |
Global_Classes_Repository | public function put( array $items, array $order ) | 全量替换(导入场景),内部自动 diff 出 added/deleted/modified |
Global_Classes_Parser | public function parse( $data ): Parse_Result | 校验完整{ items, order }载荷 |
Global_Classes_Parser | public function parse_items( array $items ) | 仅校验 items |
Global_Classes_Parser | public function parse_order( array $order, array $final_item_ids ) | 校验/规范化顺序 |
源码位置:global-classes-repository.php、global-classes-parser.php。
all()的实现值得留意(含源码注释提醒):该方法可能较重,会阻塞直到拉取全部全局类,调用方应注意批处理与缓存。读取侧按READ_BATCH_SIZE = 100分批执行get_posts()(以meta_query的IN匹配_elementor_global_class_id),配合clean_post_cache()与wp_cache_flush_runtime()控制内存占用;写入侧同样按PERSIST_BATCH_SIZE = 100分批执行创建/更新/删除。
扩展方式:固定数据模型,只走 REST 与 MCP
Global Classes 采用固定数据模型,不提供公开注册钩子(no public registration hook)。扩展与集成必须通过以下两个受支持入口:
- REST API:
PUT elementor/v1/global-classes(含context参数区分预览/发布),详见 docs/atomic-builder/global-classes/api.md; - MCP:
elementor/manage-classes工具。
不要直接写 CPT 或 meta——绕过 Repository 的写入会破坏顺序、标签映射、ID 索引与 usage 关系的一致性。若要在自定义 Widget 上暴露classes属性,正确做法是在define_props_schema()中调用Classes_Prop_Type::make()。
REST 端点全景(源码常量API_NAMESPACE = 'elementor/v1',API_BASE = 'global-classes'):
| 端点 | 方法 | 权限 | 说明 |
|---|---|---|---|
/elementor/v1/global-classes | GET | 已登录用户 | 返回有序[{ id, label }]索引 |
/elementor/v1/global-classes | PUT | Add_Capabilities::UPDATE_CLASS | 增量更新(changes + items + order) |
/elementor/v1/global-classes/post?post_id= | GET | 已登录用户 | 单个文档实际使用的类 |
/elementor/v1/global-classes/styles?ids= | GET | 已登录用户 | 按逗号分隔 ID 批量取类 |
/elementor/v1/global-classes/usage | GET | manage_options | 类的使用统计 |
所有端点都接受context参数(frontend/preview,默认frontend)。
内部机制:读、写与关系索引
读路径
all() → 读 Kit order meta → 按 ID 分批批量取 CPT → Global_Classes_Parser::sanitize_order() → Global_Classes 值对象all_from_posts()先取 order;order 为空直接返回空值对象,否则按iterate_class_posts_for_ids()分批遍历帖子,逐条to_array()组装 items,最后sanitize_order()修复顺序。读取时若数据带版本信息,会经Migrations_Orchestrator自动执行 prop-type 数据迁移并写回(见 docs/atomic-builder/migration/prop-type-migrations.md 与Global_Class_Post::migrate_data())。
写路径
apply_changes() → 按 added/deleted/modified 分批做 CPT CRUD → 更新 order/labels meta → (非预览)同步 v3 + 清除草稿 meta → 触发 elementor/global_classes/update 动作 → (有删除且非预览)触发 elementor/global_classes/cleanupapply_changes()(global-classes-repository.php)的核心步骤:
- 依据
$changes['deleted'|'added'|'modified']计算受影响文档 ID(用于后续清理); persist_class_batch_mutations()分批执行删除(预览态清空数据、发布态删帖并清除关系)、创建(Global_Class_Post::create()并登记post_ids_map)、更新(写入数据与 label);- 重建
order与labels映射; - 非预览分支:
Global_Classes_Sync_Map同步 v3、把同一 order 写入预览态、批量清除预览 meta 与预览标签(保证发布后草稿不留残留); - 触发
elementor/global_classes/update动作(载荷含added/deleted/modified/order/affected_post_ids);存在删除且非预览时再触发elementor/global_classes/cleanup。
put()是导入专用路径:先与当前状态 diff 出增删改,收集受影响文档后再put_to_posts()全量落库,同样触发 update/cleanup 动作。源码注释特别说明了"预先收集受影响文档"的原因:更新机制在批处理迭代时会顺带处理Global_Classes_Relations,进入清理阶段时关系已不存在;提前收集还能避免同一文档被 N 个被删类重复遍历 N 次。
关系索引(Relations)
Global_Classes_Relations(global-classes-relations.php)在文档保存时(钩子elementor/document/after_save)重建"文档 ↔ 类"的双向索引:
- 正向:文档 meta
_elementor_used_global_class(预览态_elementor_used_global_class_preview)记录该文档用到的类 ID; - 反向:类 CPT 的 meta
_elementor_global_class_using_documents记录哪些文档使用该类,供删除时快速定位受影响文档; - 索引标记:
_elementor_global_class_usage_indexed(预览态后缀_preview)避免重复全量扫描。
正是这套索引支撑了 REST 的/global-classes/post、/global-classes/styles端点,以及删除类时的affected_post_ids清理通知。删除类的清理动作(elementor/global_classes/cleanup)由模块监听后移除文档中的类引用,详见 global-classes-cleanup.php。
数据模型相关的其他文档
- docs/atomic-builder/global-classes/overview.md:模块全景、实验开关
e_classes与两种"classes"概念辨析; - docs/atomic-builder/global-classes/api.md:REST 与 MCP 完整接口;
- docs/atomic-builder/global-classes/applying-classes.md:类如何挂接到元素并解析为 CSS;
- docs/atomic-builder/fundamentals/style-schema.md:variants 与 props 的校验 Schema;
- docs/atomic-builder/migration/prop-type-migrations.md:读取期的 prop-type 数据迁移机制。
【免费下载链接】elementorThe most advanced frontend drag & drop page builder. Create high-end, pixel perfect websites at record speeds. Any theme, any page, any design.项目地址: https://gitcode.com/GitHub_Trending/el/elementor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考