关于 hyva-cms-custom-field
为开发者提供创建 Hyvä CMS 自定义字段类型及字段处理器的技能,用于扩展编辑器输入控件。它引导选择基础字段、内联处理器或模态处理器,并生成模板和注册配置。
主要功能
- 01支持基础字段、内联和模态处理器
- 02生成字段及处理器模板
- 03配置字段类型注册
- 04为模态处理器添加布局注册
- 05提供字段使用示例
适用场景
- 01为编辑器添加颜色选择控件
- 02创建产品选择或链接配置模态框
- 03为自定义输入实现验证逻辑
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ hyva-themes/ hyva-ai-tools --skill hyva-cms-custom-field安装后在 Claude Code 中输入/hyva-cms-custom-field确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/templates/basic-field.phtml.tplinline-handler.phtml.tplmodal-field.phtml.tplmodal-handler.phtml.tplreferences/built-in-handlers.mdhandler-communication.mdhandler-patterns.mdtemplate-requirements.md详细介绍
Hyvä CMS 自定义字段类型创建工具
概述
此技能指导如何为 Hyvä CMS 组件创建自定义字段类型和字段处理器。自定义字段类型扩展内置字段类型(文本、文本区域、选择框等),为 CMS 编辑器界面提供专用输入控件。
两种自定义字段:
- 基本自定义字段类型:可直接输入数据的自定义输入控件(例如日期范围、颜色选择器、自定义验证)
- 字段处理器:带有复杂交互的增强界面(例如带图片的产品选择器、可搜索下拉菜单、链接配置模态框)
**命令执行:**对于需要在开发环境中运行的命令(例如 bin/magento),请使用 hyva-exec-shell-cmd 技能检测环境并确定合适的命令包装器。
工作流
步骤 1:选择模块
如果提示中尚未指定,请确定要在哪个位置创建自定义字段类型:
选项 A:新建模块
使用 hyva-create-module 技能,并设置:
dependencies:["Hyva_CmsBase", "Hyva_CmsLiveviewEditor"]composer_require:{"hyva-themes/commerce-module-cms": "^1.0"}
选项 B:现有模块
确认模块具有所需依赖项:
etc/module.xml中包含Hyva_CmsBase和Hyva_CmsLiveviewEditorcomposer.json中包含hyva-themes/commerce-module-cms
如有缺失,请添加相应依赖项。
步骤 2:字段类型详情
收集有关自定义字段类型的信息:
- 字段类型名称(小写标识符,例如
date_range、product_selector、color_picker) - 用途(它收集什么数据?)
- UI 模式:
- 基本字段:带验证的简单输入(日期选择器、模式输入框、增强文本字段)
- 内联处理器:在字段区域中显示的增强控件(可搜索下拉菜单、颜色选择器)
- 模态框处理器:用于复杂选择的独立对话框(产品选择器、链接构建器、媒体库)
- 数据结构(简单字符串、JSON 对象、数组?)
- 验证要求(模式、必填、自定义规则?)
步骤 3:选择实现模式
根据步骤 2 中确定的 UI 模式:
模式 A:基本自定义字段类型
适用于需要自定义 HTML5 验证或专用输入控件的简单输入:
- 单个字段模板文件
- 无独立处理器模态框
- 示例:日期范围选择器、自定义模式验证、滑块输入
模式 B:内联字段处理器
适用于保留在字段区域中的增强控件:
- 带有 Alpine.js 组件的单个模板文件
- 无独立处理器模态框
- 示例:可搜索下拉选择框、带色板的颜色选择器
模式 C:基于模态框的字段处理器
适用于需要更大空间的复杂选择界面:
- 字段模板(显示所选内容和触发按钮)
- 处理器模态框模板(包含完整 UI 的独立对话框)
- 用于注册处理器的布局 XML
- 示例:产品选择器、链接配置、媒体库
有关每种类型的详细实现模式和代码示例,请参阅 references/handler-patterns.md。
步骤 4:生成字段模板
在 view/adminhtml/templates/field-types/[field-type-name].phtml 创建字段模板。
模板必需元素:
- 字段容器,且 ID 正确:
field-container-{uid}_{fieldName} - 名称为
{uid}_{fieldName}的输入元素 - 验证消息容器:
validation-messages-{uid}_{fieldName} - 值更改时调用
updateWireField()或updateField() - 通过
$magewire->errors处理错误状态 - **重要:**使用空值合并运算符获取字段值:
$block->getData('value') ?? ''(不要进行类型转换)
使用 assets/templates/ 中合适的模板:
basic-field.phtml.tpl- 基本自定义字段类型inline-handler.phtml.tpl- 内联增强控件modal-field.phtml.tpl- 模态框处理器字段模板
有关详细的模板要求和模式,请参阅 references/template-requirements.md。
步骤 5:生成处理器模态框(如需要)
仅对于基于模态框的处理器,在 view/adminhtml/templates/handlers/[handler-name]-handler.phtml 创建处理器模板。
处理器模态框结构:
- 使用 Alpine.js 组件和
open:flex类的<dialog>元素(不要使用静态的flex) - 监听来自字段模板的初始化事件
- 实现选择界面(搜索、筛选、网格等)
- 保存时分发
editor-change事件
以 assets/templates/modal-handler.phtml.tpl 为起始模板。
请参阅 references/handler-communication.md,了解事件协议和数据交换模式。
步骤 6:注册字段类型
在 etc/adminhtml/di.xml 中添加注册项:
<type name="Hyva\CmsLiveviewEditor\Model\CustomField">
<arguments>
<argument name="customTypes" xsi:type="array">
<item name="[field_type_name]" xsi:type="string">
[Vendor]_[Module]::field-types/[field-type-name].phtml
</item>
</argument>
</arguments>
</type>
步骤 7:注册处理器模态框(如需要)
仅对于基于模态框的处理器,创建或更新 view/adminhtml/layout/liveview_editor.xml:
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="before.body.end">
<block name="[handler_name]_handler"
template="[Vendor]_[Module]::handlers/[handler-name]-handler.phtml"/>
</referenceContainer>
</body>
</page>
注意: 内联处理器不需要在布局 XML 中注册。
步骤 8:使用示例
在 components.json 中提供自定义字段类型的使用示例:
{
"my_component": {
"label": "My Component",
"content": {
"[field_name]": {
"type": "custom_type",
"custom_type": "[field_type_name]",
"label": "Field Label",
"attributes": {
"required": true,
"pattern": ".*"
}
}
}
}
}
资源
references/template-requirements.md
自定义字段类型模板要求的完整参考:
- 必需的标记模式和元素 ID
- 字段容器结构
- 验证消息容器
- 字段值更新方法(
updateWireField与updateField) - HTML5 验证属性
- 错误状态处理
实现字段模板时请阅读此文件,以确保与 CMS 编辑器正确集成。
references/handler-patterns.md
三种自定义字段类型的实现模式:
- 基础自定义字段类型(简单输入)
- 内联字段处理器(增强控件)
- 基于模态框的字段处理器(对话框选择)
每种模式都包含:
- 完整代码示例
- 各模式的适用场景
- Alpine.js 组件结构
- 数据流和状态管理
选择实现模式并编写模板代码时请阅读此文件。
references/handler-communication.md
字段处理器的事件协议和数据交换:
- 初始化事件结构
- 保存事件结构
- 字段值编码和解码
- 错误处理模式
- 常见问题及解决方案
实现处理器模态框时请阅读此文件,以了解通信协议。
references/built-in-handlers.md
Hyvä CMS 内置字段处理器参考:
- Product Handler(基于模态框,图片网格选择)
- Link Handler(基于模态框,多类型链接配置)
- Searchable Select(内联增强下拉菜单)
每种处理器都包含:
- 在 Hyvä CMS 模块中的位置
- 主要功能和模式
- 使用示例
- 可供研究实现模式的代码
查找实现示例或可复制的模式时请阅读此文件。
assets/templates/basic-field.phtml.tpl
带有自定义验证或输入控件的基础自定义字段类型模板。
占位符:
{{FIELD_TYPE_NAME}}- 自定义字段类型标识符{{FIELD_INPUTS}}- 输入元素 HTML{{VALIDATION_LOGIC}}- 自定义验证 JavaScript(可选)
assets/templates/inline-handler.phtml.tpl
内联增强控件模板(可搜索下拉菜单、颜色选择器等)。
占位符:
{{HANDLER_NAME}}- Alpine.js 组件名称{{HANDLER_LOGIC}}- Alpine.js 组件实现{{HANDLER_UI}}- 增强控件 HTML
assets/templates/modal-field.phtml.tpl
基于模态框的处理器字段模板(触发按钮 + 隐藏输入框)。
占位符:
{{EVENT_NAME}}- 要分发的自定义事件名称{{BUTTON_LABEL}}- 按钮文本{{DISPLAY_VALUE}}- 当前选项的显示内容
assets/templates/modal-handler.phtml.tpl
基于模态框的选择界面处理器模板。
占位符:
{{HANDLER_NAME}}- Alpine.js 组件名称{{MODAL_TITLE}}- 对话框标题文本{{SELECTION_UI}}- 选择界面 HTML{{SAVE_LOGIC}}- 保存按钮逻辑
重要指南
核心要求
- 模板要求:所有自定义字段类型都必须遵循规定的标记模式(容器 ID、输入名称、验证消息)
- 处理器注册:模态框处理器需要在布局 XML 中注册;内联处理器无需注册
- 验证:通过
$filteredAttributes应用 HTML5 验证属性,以实现自动验证 - Alpine 组件:如果使用自定义 Alpine 组件,请将输入字段置于组件之外,并通过原生 JavaScript 更新
- 内置示例:参考
Hyva_CmsLiveviewEditor::page/js/中的内置处理器,了解经过验证的模式
代码库中的准确模式
根据内置的 Hyvä CMS 处理器实现:
-
事件命名约定:使用
toggle-{type}-select模式- ✅ 正确:
toggle-product-select、toggle-link-select、toggle-category-select - ❌ 错误:
toggle-product-handler、toggle-link-handler
- ✅ 正确:
-
处理器函数命名:使用
init{Type}Select()模式- ✅ 示例:
initProductSelect()、initLinkSelect()、initCategorySelect()
- ✅ 示例:
-
字段值更新方法:
- 使用
updateWireField(默认):Products、Link、Category 处理器- 通过 Magewire 立即触发服务端验证
- 保持组件状态同步
- 使用
updateField(特定情况):Image 处理器、使用防抖的输入项(color、range)- 更新预览,但不进行服务器往返
- 延迟到保存时再验证
- 使用
-
JSON 编码模式:所有复杂数据(数组、对象)都必须进行 JSON 编码
// Field template value="<?= $escaper->escapeHtmlAttr(json_encode($fieldValue)) ?>" // Handler initialization const data = JSON.parse(fieldValue); // @change handler @change="updateWireField(..., JSON.parse($event.target.value))" -
为兼容 Livewire 使用 wire:ignore:可搜索的选择框使用
wire:ignore包装器<div wire:ignore> <div x-data="initSearchableSelect(...)"> </div> </div> -
单独的处理器文件:即使是内联处理器,也可能有单独的函数文件
- 字段模板:
liveview/field-types/searchable_select.phtml - 处理器函数:
page/js/searchable-select-handler.phtml
- 字段模板:
-
Icons 视图模型:用于 UI 元素
/** @var Icons $icons */ $icons = $viewModels->require(Icons::class); <?= /** @noEscape */ $icons->trashHtml('', 22, 22) ?> -
FieldTypes 视图模型:用于属性筛选
/** @var FieldTypes $fieldTypes */ $fieldTypes = $viewModels->require(FieldTypes::class); $filteredAttributes = $fieldTypes->getDefinedFieldAttributes($attributes); // Or for specific attributes: $filteredAttributes = $fieldTypes->getAttributesByKeys($attributes, ['required', 'data-required']); -
关键:布局 XML referenceContainer:处理器模态框必须使用
before.body.end容器- ✅ 正确:
<referenceContainer name="before.body.end"> - ❌ 错误:
<referenceContainer name="content"> before.body.end容器可确保处理器模态框在页面正文末尾加载,这是 Alpine.js 正常初始化和模态框功能正常运行的必要条件
- ✅ 正确:
-
关键:字段值类型处理:绝不要对字段值进行类型转换,始终使用 null 合并运算符
- ✅ 正确:
$fieldValue = $block->getData('value') ?? ''; - ❌ 错误:
$fieldValue = (string) $block->getData('value'); - 值为
null时,类型转换(string)会失败并导致 PHP 错误 - 字符串值使用
?? '',数组值使用?? [],其他数据类型则使用适当的默认值 - 参考:请参见内置字段类型,例如使用
?? []模式的category.phtml
- 关键:对话框模态框类:处理器模态框必须使用
open:flex,不能使用静态的flex类
- ✅ 正确:
<dialog class="... open:flex flex-col"> - ❌ 错误:
<dialog class="... flex flex-col">(模态框始终可见) open:前缀仅在对话框打开时(原生 HTML 对话框状态)应用样式- 参考:请参见内置处理器,例如使用
open:flex flex-col的category-handler.phtml
- 关键:复杂数据类型处理:对于存储 JSON/数组数据的字段,必须同时处理数组和字符串类型
- 字段值可能以已解码的数组形式返回,也可能以 JSON 字符串形式返回(取决于存储方式和上下文)
- ✅ 正确模式:
$data = ['default' => 'structure']; if ($fieldValue) { if (is_array($fieldValue)) { $data = $fieldValue; // 已解码 } elseif (is_string($fieldValue)) { $decoded = json_decode($fieldValue, true); if (is_array($decoded)) { $data = $decoded; } } } // 输出到隐藏输入框时,始终确保其为 JSON 字符串 $fieldValueJson = is_array($fieldValue) ? json_encode($fieldValue) : $fieldValue; - ❌ 错误:未检查类型就使用
json_decode($fieldValue)(如果值已经是数组,则会失败) - ❌ 错误:未先进行 JSON 编码,就直接在
value属性中使用数组