hyva-cms-custom-field by hyva-themes/hyva-ai-tools
npx skills add https://github.com/hyva-themes/hyva-ai-tools --skill hyva-cms-custom-field本技能指导为 Hyvä CMS 组件创建自定义字段类型和字段处理器。自定义字段类型扩展了内置字段类型(文本、文本区域、选择等),为 CMS 编辑器界面提供专门的输入控件。
两种类型的自定义字段:
命令执行: 对于需要在开发环境中运行的命令(例如 bin/magento),请使用 hyva-exec-shell-cmd 技能来检测环境并确定适当的命令包装器。
如果提示中尚未指定,请确定在何处创建自定义字段类型:
选项 A:新模块
使用 hyva-create-module 技能,并指定:
dependencies:["Hyva_CmsBase", "Hyva_CmsLiveviewEditor"]广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
composer_require:{"hyva-themes/commerce-module-cms": "^1.0"}选项 B:现有模块
验证模块是否具有所需的依赖项:
etc/module.xml 中包含 Hyva_CmsBase 和 Hyva_CmsLiveviewEditorcomposer.json 中包含 hyva-themes/commerce-module-cms如果需要,请添加缺失的依赖项。
收集有关自定义字段类型的信息:
date_range、product_selector、color_picker)根据步骤 2 中确定的 UI 模式:
模式 A:基础自定义字段类型
适用于具有自定义 HTML5 验证或专用输入控件的简单输入:
模式 B:内联字段处理器
适用于保留在字段区域内的增强型控件:
模式 C:基于模态框的字段处理器
适用于需要更多空间的复杂选择界面:
有关每种类型的详细实现模式和代码示例,请参阅 references/handler-patterns.md。
在 view/adminhtml/templates/field-types/[field-type-name].phtml 处创建字段模板。
必需的模板元素:
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。
仅适用于基于模态框的处理器,在 view/adminhtml/templates/handlers/[handler-name]-handler.phtml 处创建处理器模板。
处理器模态框结构:
open:flex 类的 <dialog> 元素(不要使用静态 flex)editor-change 事件使用 assets/templates/modal-handler.phtml.tpl 作为起点。
有关事件协议和数据交换模式,请参阅 references/handler-communication.md。
将注册信息添加到 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>
仅适用于基于模态框的处理器,创建或更新 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 注册。
在 components.json 中提供使用自定义字段类型的示例:
{
"my_component": {
"label": "我的组件",
"content": {
"[field_name]": {
"type": "custom_type",
"custom_type": "[field_type_name]",
"label": "字段标签",
"attributes": {
"required": true,
"pattern": ".*"
}
}
}
}
}
自定义字段类型模板要求的完整参考:
updateWireField 与 updateField)在实现字段模板时阅读此文件,以确保与 CMS 编辑器的正确集成。
所有三种自定义字段类型的实现模式:
每种模式包括:
在选择实现模式和编写模板代码时阅读此文件。
字段处理器的事件协议和数据交换:
在实现处理器模态框时阅读此文件,以了解通信协议。
Hyvä CMS 内置字段处理器的参考:
每个包括:
在寻找实现示例或要复制的模式时阅读此文件。
用于具有自定义验证或输入控件的基础自定义字段类型的模板。
占位符:
{{FIELD_TYPE_NAME}} - 自定义字段类型标识符{{FIELD_INPUTS}} - 输入元素 HTML{{VALIDATION_LOGIC}} - 自定义验证 JavaScript(可选)用于内联增强型控件(可搜索下拉菜单、颜色选择器等)的模板。
占位符:
{{HANDLER_NAME}} - Alpine.js 组件名称{{HANDLER_LOGIC}} - Alpine.js 组件实现{{HANDLER_UI}} - 增强型控件 HTML用于基于模态框的处理器的字段模板(触发按钮 + 隐藏输入)。
占位符:
{{EVENT_NAME}} - 要派发的自定义事件名称{{BUTTON_LABEL}} - 按钮文本{{DISPLAY_VALUE}} - 当前选择显示用于基于模态框的选择界面的处理器模态框模板。
占位符:
{{HANDLER_NAME}} - Alpine.js 组件名称{{MODAL_TITLE}} - 对话框标题文本{{SELECTION_UI}} - 选择界面 HTML{{SAVE_LOGIC}} - 保存按钮逻辑$filteredAttributes 应用 HTML5 验证属性以进行自动验证Hyva_CmsLiveviewEditor::page/js/ 中的内置处理器以获取经过验证的模式基于内置的 Hyvä CMS 处理器实现:
事件命名约定:使用 toggle-{type}-select 模式
toggle-product-select、toggle-link-select、toggle-category-selecttoggle-product-handler、toggle-link-handler处理器函数命名:使用 init{Type}Select() 模式
initProductSelect()、initLinkSelect()、initCategorySelect()字段值更新方法:
updateWireField(默认):产品、链接、类别处理器
updateField(专用):图像处理器、防抖输入(颜色、范围)
JSON 编码模式:所有复杂数据(数组、对象)必须进行 JSON 编码
// 字段模板
value="<?= $escaper->escapeHtmlAttr(json_encode($fieldValue)) ?>"
// 处理器初始化
const data = JSON.parse(fieldValue);
// @change 处理器
@change="updateWireField(..., JSON.parse($event.target.value))"
Livewire 兼容性的 wire:ignore:可搜索选择使用 wire:ignore 包装器
<div wire:ignore>
<div x-data="initSearchableSelect(...)">
<!-- Alpine 组件 -->
</div>
</div>
独立的处理器文件:即使是内联处理器也可能有单独的函数文件
liveview/field-types/searchable_select.phtmlpage/js/searchable-select-handler.phtml图标视图模型:用于 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);
// 或用于特定属性:
$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 初始化和模态框功能正常运行所必需的关键:字段值类型处理:绝不使用类型转换来处理字段值,始终使用空值合并运算符
$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 对话框状态)category-handler.phtml,它们使用 open:flex flex-col字段值可能作为已解码的数组返回,也可能作为 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) 不进行类型检查(如果值已经是数组,则会失败)
❌ 不正确:在 value 属性中直接使用数组而不先进行 JSON 编码
每周安装数
159
仓库
GitHub 星标数
59
首次出现
2026年1月29日
安全审计
安装于
github-copilot158
opencode151
codex151
gemini-cli149
amp149
kimi-cli149
This skill guides the creation of custom field types and field handlers for Hyvä CMS components. Custom field types extend the built-in field types (text, textarea, select, etc.) with specialized input controls for the CMS editor interface.
Two types of custom fields:
Command execution: For commands that need to run inside the development environment (e.g., bin/magento), use the hyva-exec-shell-cmd skill to detect the environment and determine the appropriate command wrapper.
If not already specified in the prompt, determine where to create the custom field type:
Option A: New Module
Use the hyva-create-module skill with:
dependencies: ["Hyva_CmsBase", "Hyva_CmsLiveviewEditor"]composer_require: {"hyva-themes/commerce-module-cms": "^1.0"}Option B: Existing Module
Verify the module has required dependencies:
Hyva_CmsBase and Hyva_CmsLiveviewEditor in etc/module.xmlhyva-themes/commerce-module-cms in composer.jsonAdd missing dependencies if needed.
Gather information about the custom field type:
date_range, product_selector, color_picker)Based on the UI pattern identified in Step 2:
Pattern A: Basic Custom Field Type
For simple inputs with custom HTML5 validation or specialized input controls:
Pattern B: Inline Field Handler
For enhanced controls that remain in the field area:
Pattern C: Modal-Based Field Handler
For complex selection interfaces requiring more space:
See references/handler-patterns.md for detailed implementation patterns and code examples for each type.
Create the field template at view/adminhtml/templates/field-types/[field-type-name].phtml.
Required template elements:
field-container-{uid}_{fieldName}{uid}_{fieldName}validation-messages-{uid}_{fieldName}updateWireField() or updateField() call on value change$magewire->errors$block->getData('value') ?? '' (NOT type casting)Use the appropriate template from assets/templates/:
basic-field.phtml.tpl - Basic custom field typeinline-handler.phtml.tpl - Inline enhanced controlmodal-field.phtml.tpl - Modal handler field templateSee references/template-requirements.md for detailed template requirements and patterns.
For modal-based handlers only, create the handler template at view/adminhtml/templates/handlers/[handler-name]-handler.phtml.
Handler modal structure:
<dialog> element with Alpine.js component and open:flex class (NOT static flex)editor-change event on saveUse assets/templates/modal-handler.phtml.tpl as the starting point.
See references/handler-communication.md for event protocols and data exchange patterns.
Add registration to 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>
For modal-based handlers only, create or update 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>
Note: Inline handlers do NOT require layout XML registration.
Provide an example of using the custom field type in 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": ".*"
}
}
}
}
}
Complete reference for custom field type template requirements:
updateWireField vs updateField)Read this file when implementing the field template to ensure proper integration with the CMS editor.
Implementation patterns for all three custom field types:
Each pattern includes:
Read this file when selecting the implementation pattern and writing the template code.
Event protocols and data exchange for field handlers:
Read this file when implementing handler modals to understand the communication protocol.
Reference for Hyvä CMS built-in field handlers:
Each includes:
Read this file when looking for implementation examples or patterns to copy.
Template for basic custom field types with custom validation or input controls.
Placeholders:
{{FIELD_TYPE_NAME}} - Custom field type identifier{{FIELD_INPUTS}} - Input element(s) HTML{{VALIDATION_LOGIC}} - Custom validation JavaScript (optional)Template for inline enhanced controls (searchable dropdown, color picker, etc.).
Placeholders:
{{HANDLER_NAME}} - Alpine.js component name{{HANDLER_LOGIC}} - Alpine.js component implementation{{HANDLER_UI}} - Enhanced control HTMLField template for modal-based handlers (trigger button + hidden input).
Placeholders:
{{EVENT_NAME}} - Custom event name to dispatch{{BUTTON_LABEL}} - Button text{{DISPLAY_VALUE}} - Current selection displayHandler modal template for modal-based selection interfaces.
Placeholders:
{{HANDLER_NAME}} - Alpine.js component name{{MODAL_TITLE}} - Dialog header text{{SELECTION_UI}} - Selection interface HTML{{SAVE_LOGIC}} - Save button logic$filteredAttributes for automatic validationHyva_CmsLiveviewEditor::page/js/ for proven patternsBased on built-in Hyvä CMS handler implementations:
Event Naming Convention : Use toggle-{type}-select pattern
toggle-product-select, toggle-link-select, toggle-category-selecttoggle-product-handler, toggle-link-handlerHandler Function Naming : Use init{Type}Select() pattern
initProductSelect(), initLinkSelect(), $fieldValue = $block->getData('value') ?? '';$fieldValue = (string) $block->getData('value');(string) will fail when value is null, causing PHP errors?? '' for string values, ?? [] for array values, or appropriate default for your data typecategory.phtml which use ?? [] patternopen:flex not static flex class<dialog class="... open:flex flex-col"><dialog class="... flex flex-col"> (modal always visible)open: prefix applies styles only when dialog is open (native HTML dialog state)category-handler.phtml which use open:flex flex-colField values may be returned as already-decoded arrays OR as JSON strings (depends on storage/context)
✅ Correct pattern:
$data = ['default' => 'structure'];
if ($fieldValue) {
if (is_array($fieldValue)) {
$data = $fieldValue; // Already decoded
} elseif (is_string($fieldValue)) {
$decoded = json_decode($fieldValue, true);
if (is_array($decoded)) {
$data = $decoded;
}
}
}
// When outputting to hidden input, ALWAYS ensure it's a JSON string
$fieldValueJson = is_array($fieldValue) ? json_encode($fieldValue) : $fieldValue;
❌ Incorrect: json_decode($fieldValue) without type checking (fails if value is already an array)
❌ Incorrect: Using array directly in value attribute without JSON-encoding first
Weekly Installs
159
Repository
GitHub Stars
59
First Seen
Jan 29, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot158
opencode151
codex151
gemini-cli149
amp149
kimi-cli149
Laravel安全最佳实践指南 | 防范CSRF、SQL注入、XSS攻击
906 周安装
initCategorySelect()Field Value Update Methods :
updateWireField (default): Products, Link, Category handlers
updateField (specialized): Image handler, debounced inputs (color, range)
JSON Encoding Pattern : All complex data (arrays, objects) must be JSON-encoded
// Field template
value="<?= $escaper->escapeHtmlAttr(json_encode($fieldValue)) ?>"
// Handler initialization
const data = JSON.parse(fieldValue);
// @change handler
@change="updateWireField(..., JSON.parse($event.target.value))"
wire:ignore for Livewire Compatibility : Searchable select uses wire:ignore wrapper
<div wire:ignore>
<div x-data="initSearchableSelect(...)">
<!-- Alpine component -->
</div>
</div>
Separate Handler Files : Even inline handlers may have separate function files
liveview/field-types/searchable_select.phtmlpage/js/searchable-select-handler.phtmlIcons View Model : Use for UI elements
/** @var Icons $icons */
$icons = $viewModels->require(Icons::class);
<?= /** @noEscape */ $icons->trashHtml('', 22, 22) ?>
FieldTypes View Model : Use for attribute filtering
/** @var FieldTypes $fieldTypes */
$fieldTypes = $viewModels->require(FieldTypes::class);
$filteredAttributes = $fieldTypes->getDefinedFieldAttributes($attributes);
// Or for specific attributes:
$filteredAttributes = $fieldTypes->getAttributesByKeys($attributes, ['required', 'data-required']);
CRITICAL: Layout XML referenceContainer : Handler modals MUST use before.body.end container
<referenceContainer name="before.body.end"><referenceContainer name="content">before.body.end container ensures the handler modal is loaded at the end of the page body, which is required for proper Alpine.js initialization and modal functionalityCRITICAL: Field Value Type Handling : NEVER use type casting for field values, always use null coalescing operator