mcpField
H
hyva-cms-component

hyva-cms-component

github.com/hyva-themes开发85766 次安装13 周前更新
代码仓库

关于 hyva-cms-component

为 Magento 2 项目交互式创建 Hyvä CMS 自定义组件的技能,面向开发者。它涵盖模块选择、字段配置、模板生成和升级设置。

主要功能

  1. 01在新建或现有模块中创建组件
  2. 02提供常用字段预设与自定义字段
  3. 03配置模板变体及子组件
  4. 04生成组件 JSON 与 PHTML 模板
  5. 05执行 Magento 升级设置

适用场景

  1. 01为 CMS 编辑器添加内容组件
  2. 02在现有模块中注册组件
  3. 03创建含字段和模板变体的组件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/hyva-themes/hyva-ai-tools --skill hyva-cms-component

安装后在 Claude Code 中输入/hyva-cms-component确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/templates/component/
template.phtml.tpl
references/
component-schema.md
critical-patterns.md
example-component.md
field-types.md
troubleshooting.md
variant-support.md

详细介绍

Hyvä CMS 组件创建器

概述

此技能将指导你以交互方式为 Magento 2 创建自定义 Hyvä CMS 组件。它支持在新模块或现有模块中创建组件,并提供适用于常见模式的字段预设,以及自动执行 setup:upgrade。

**命令执行:**对于需要在开发环境中运行的命令(例如 bin/magento),请使用 hyva-exec-shell-cmd 技能检测环境并确定合适的命令包装器。

工作流

第 1 步:选择模块

如果提示词中尚未指定,请询问用户要在哪里创建组件:

选项 A:新建模块

询问以下两项(不要在未询问的情况下假定默认值):

  1. 供应商名称(例如 Acme)- 必填,无默认值。不要建议供应商名称,请用户自行输入。
  2. 模块名称 - 建议默认使用 CmsComponents,用户可按 Enter 接受

然后使用 hyva-create-module 技能,并设置:

  • dependencies:["Hyva_CmsBase"]
  • composer_require:{"hyva-themes/commerce-module-cms": "^1.0"}

选项 B:现有模块

  • 请求模块路径(可以位于 app/code/、vendor/ 或自定义位置)
  • 检查模块是否在 etc/module.xml 中将 Hyva_CmsBase 声明为依赖项。如果没有,请添加。
  • 检查模块是否在 composer.json 中将 hyva-themes/commerce-module-cms 声明为依赖项。如果没有,请添加。
第 2 步:组件详情

收集组件信息:

  1. 组件名称(snake_case,例如 feature_card)

  2. 标签(在编辑器中显示的名称,例如“Feature Card”)

  3. 类别(Layout、Elements、Media、Content 或 Other)

  4. 图标 - 自动选择合适的图标:

    第 4a 步:识别已在使用的图标 使用 hyva-cms-components-dump 技能导出所有当前 CMS 组件。从输出中提取所有 icon 值,整理出一份现有组件已使用的图标列表。

    第 4b 步:查找可用的 lucide 图标 列出 vendor/hyva-themes/magento2-theme-module/src/view/base/web/svg/lucide/ 中的 SVG 文件,以获取所有可用图标。

    第 4c 步:选择最合适的图标 从可用且未被其他组件使用的 lucide 图标中:

    • 选择名称最符合新组件用途或含义的图标
    • 考虑语义含义(例如,购物车相关组件使用 shopping-cart.svg,图像相关组件使用 image.svg,网格布局使用 layout-grid.svg)
    • 将所选图标格式化为 Hyva_Theme::svg/lucide/[icon-name].svg

    如果找不到合适且未被使用的图标,或 lucide 目录不存在,则不设置 icon 属性。

第 3 步:选择字段

提供字段预设或自定义字段创建选项。可用预设(Basic Card、Image Card、CTA Block、Text Block、Feature Item、Testimonial、Accordion Item)请参阅 references/field-types.md 中的“Field Presets”部分;也可以允许用户自定义字段定义。

**权威字段类型:**如果有可用的项目实时列表,应优先使用该列表——通过 hyva-exec-shell-cmd 包装器运行 bin/magento hyva:cms:list-fields(核心和自定义字段类型)。 仅当该命令不可用时,才使用 references/field-types.md 作为备用。

对于自定义字段,逐一询问以下信息:

  1. 字段名称(snake_case)
  2. 字段类型(参阅 references/field-types.md)
  3. 标签
  4. 默认值(可选)
  5. 是否必填?(是/否)- 注意:这将添加为 attributes.required,而不是直接作为字段属性
  6. 其他属性(这些将放入 attributes 对象中)
第 4 步:变体支持

询问组件是否需要模板变体:

  • 如果需要:收集变体名称和标签(例如 default、compact、wide)。配置详情请参阅 references/variant-support.md。
  • 如果不需要:使用单个模板
第 5 步:生成文件

创建所需文件:

对于新模块

hyva-create-module 技能会创建基础模块结构。然后添加 CMS 专用目录:

app/code/[Vendor]/[Module]/
├── registration.php          # Created by hyva-create-module
├── composer.json             # Created by hyva-create-module
├── etc/
│   ├── module.xml            # Created by hyva-create-module
│   └── hyva_cms/
│       └── components.json   # Create this
└── view/
    └── frontend/
        └── templates/
            └── elements/
                └── [component-name].phtml (or [component-name]/ for variants)
对现有模块

创建或更新:

  • etc/hyva_cms/components.json(如果已存在,则与现有内容合并)
  • view/frontend/templates/elements/[component-name].phtml
第 6 步:运行设置

创建文件后,使用 hyva-exec-shell-cmd 技能检测到的相应命令包装器运行 bin/magento setup:upgrade。

文件生成详情

components.json 结构
{
    "[component_name]": {
        "label": "[Label]",
        "category": "[Category]",
        "template": "[Vendor]_[Module]::elements/[component-name].phtml",
        "content": {
            // Generated fields
        },
        "design": {
            "includes": [
                "Hyva_CmsBase::etc/hyva_cms/default_design.json",
                "Hyva_CmsBase::etc/hyva_cms/default_design_typography.json"
            ]
        },
        "advanced": {
            "includes": [
                "Hyva_CmsBase::etc/hyva_cms/default_advanced.json"
            ]
        }
    }
}
有效的组件属性

重要: 组件级别只允许使用特定属性。完整的架构参考请参阅 references/component-schema.md。

主要属性:label(必填)、category、template、icon、children、require_parent、content、design、advanced、disabled、custom_properties。

会导致架构错误的无效属性:

  • hidden - 不存在此属性。子组件专用的组件请使用 require_parent: true,或使用 disabled: true
  • 架构参考中未列出的任何属性
子组件配置(重要)

重要: children 是组件的顶层属性,不是 content、design 或 advanced 中的字段类型。

错误 ❌:

{
    "my_component": {
        "content": {
            "items": {
                "type": "children",
                "label": "Items"
            }
        }
    }
}

正确 ✅:

{
    "my_component": {
        "label": "My Component",
        "children": {
            "config": {
                "accepts": ["child_component"],
                "max_children": 10
            }
        },
        "content": {
            "title": {
                "type": "text",
                "label": "Title"
            }
        }
    }
}

在模板中,通过 $block->getData('children') 访问子组件,而不是通过自定义字段名称访问。

字段验证(重要)

重要: required 等字段验证属性必须放在 attributes 对象中,而不是作为字段的直接属性。

错误 ❌:

{
    "title": {
        "type": "text",
        "label": "Title",
        "required": true
    }
}

正确 ✅:

{
    "title": {
        "type": "text",
        "label": "Title",
        "attributes": {
            "required": true
        }
    }
}

其他应放在 attributes 中的验证属性:

  • required(布尔值)
  • minlength / maxlength(字符串)
  • min / max(用于数字)
  • pattern(正则表达式字符串)
  • placeholder(字符串)
  • comment(帮助文本)
  • 用于验证消息的自定义数据属性
子组件专用组件

对于只能作为其他组件子组件使用的组件(例如列表项),请使用 require_parent: true:

{
    "my_list_item": {
        "label": "My List Item",
        "category": "Elements",
        "require_parent": true,
        "template": false,
        "content": {
            "title": {"type": "text", "label": "Title"}
        }
    },
    "my_list": {
        "label": "My List",
        "category": "Elements",
        "template": "Vendor_Module::elements/my-list.phtml",
        "children": {
            "config": {
                "accepts": ["my_list_item"]
            }
        }
    }
}

当 template: false 时,父组件会直接渲染子组件数据(不使用 $block->createChildHtml())。请参阅下文“使用 template: false 渲染子组件”。

PHTML 模板结构

每个模板都必须以以下头部开头:

<?php
declare(strict_types=1);

use Hyva\CmsLiveviewEditor\Block\Element;
use Hyva\Theme\Model\ViewModelRegistry;
use Magento\Framework\Escaper;

/** @var Element $block */
/** @var Escaper $escaper */
/** @var ViewModelRegistry $viewModels */

其他要求:

  1. 在根元素上使用 $block->getEditorAttrs()
  2. 在可编辑元素上使用 $block->getEditorAttrs('field_name')
  3. 使用 $escaper->escapeHtml() 和 $escaper->escapeHtmlAttr() 进行正确的转义
按字段类型划分的模板模式

文本字段:

$title = $block->getData('title');
// In template:
<?php if ($title): ?>
    <h2 <?= /** @noEscape */ $block->getEditorAttrs('title') ?>>
        <?= $escaper->escapeHtml($title) ?>
    </h2>
<?php endif; ?>

富文本/HTML 字段:

$content = $block->getData('content');
// In template (no escaping for richtext):
<?php if ($content): ?>
    <div <?= /** @noEscape */ $block->getEditorAttrs('content') ?>>
        <?= /** @noEscape */ $content ?>
    </div>
<?php endif; ?>

图像字段:

使用 hyva-render-media-image 技能渲染图像。它提供 \Hyva\Theme\ViewModel\Media 视图模型的完整 API 参考和代码模式。

渲染图像时添加以下导入:

// Additional imports for templates with images:
use Hyva\Theme\ViewModel\Media;

/** @var Media $mediaViewModel */
$mediaViewModel = $viewModels->require(Media::class);

$block->getData('image') 中的数据可以直接传递给 getResponsivePictureHtml():

$image = $block->getData('image');

// In template:
<?php if ($image): ?>
    <?= /** @noEscape */ $mediaViewModel->getResponsivePictureHtml(
        $image,
        ['class' => 'w-full h-auto', 'loading' => 'lazy']
    ) ?>
<?php endif; ?>

有关具有独立桌面端和移动端来源的响应式图像,请参阅 hyva-render-media-image 技能。

链接字段:

$link = $block->getData('link');
$linkData = $link ? $block->getLinkData($link) : null;
// In template:
<?php if ($linkData): ?>
    <a href="<?= $escaper->escapeUrl($linkData['url']) ?>"
       <?php if (!empty($linkData['target'])): ?>target="<?= $escaper->escapeHtmlAttr($linkData['target']) ?>"<?php endif; ?>>
        <?= $escaper->escapeHtml($linkData['title'] ?: 'Read more') ?>
    </a>
<?php endif; ?>

布尔字段:

$showTitle = (bool) $block->getData('show_title');
// In template:
<?php if ($showTitle && $title): ?>
    
<?php endif; ?>

选择字段:

$style = $block->getData('style') ?: 'default';
$styleClasses = match($style) {
    'primary' => 'bg-blue-600 text-white',
    'secondary' => 'bg-gray-200 text-gray-800',
    default => 'bg-white text-gray-600'
};

子级字段(具有各自的模板):

当子组件具有各自的模板(默认行为)时,使用 $block->createChildHtml():

$children = $block->getData('children') ?: [];
// In template:
<?php foreach ($children as $index => $child): ?>
    <?= /** @noEscape */ $block->createChildHtml($child, 'child-' . $index) ?>
<?php endforeach; ?>

使用 template: false 渲染子级:

当子组件的 "template": false 时,父组件会直接渲染它们。子级数据是扁平的——字段值直接位于子级数组中,而不是嵌套在 content 键下。

$children = $block->getData('children') ?: [];

// In template - iterate and access child data directly:
<?php foreach ($children as $elementData): ?>
    <?php
    // Access fields directly on $elementData (NOT $elementData['content']['field'])
    $image = $elementData['image'] ?? null;
    $title = $elementData['title'] ?? '';
    $description = $elementData['description'] ?? '';

    // Each child has a 'uid' for editor attributes
    $childUid = $elementData['uid'];
    ?>
    <div <?= /** @noEscape */ $block->getEditorAttrs('', $childUid) ?>>
        <?php if (!empty($image['src'])): ?>
            <?php // For image rendering patterns, see the hyva-render-media-image skill ?>
            <?= /** @noEscape */ $mediaViewModel->getResponsivePictureHtml(
                [$block->getResponsiveImageData($image)],
                ['alt' => $image['alt'] ?? '', 'class' => 'w-full h-auto', 'loading' => 'lazy']
            ) ?>
        <?php endif; ?>
        <p <?= /** @noEscape */ $block->getEditorAttrs('title', $childUid) ?>>
            <?= $escaper->escapeHtml($title) ?>
        </p>
    </div>
<?php endforeach; ?>

关于 template: false 子级的要点:

  • 子级字段数据是扁平的:使用 $elementData['field_name'],而不是 $elementData['content']['field_name']
  • 每个子级都有一个用于编辑器属性的 uid 属性
  • 使用 $block->getEditorAttrs('field_name', $childUid) 启用子级字段的实时编辑
  • 在子级的根元素上使用 $block->getEditorAttrs('', $childUid)
  • 对于图像,检查 !empty($image['src']),并使用 $block->getResponsiveImageData($image) 处理图像数据
  • 有关高级图像渲染模式(响应式断点等),请参阅 hyva-render-media-image 技能

资源

references/critical-patterns.md

请先阅读此文件——其中介绍了重要模式和常见错误,包括:

  • 正确的 children 配置(根级别与字段类型)
  • 使用 attributes 正确验证字段
  • 默认值语法
  • 生成组件前的快速检查清单

生成任何组件前,请阅读此文件,以避免常见错误。

references/example-component.md

完整的端到端示例,展示了一个 Feature Card 组件,其中包括:

  • 完整的 components.json 定义
  • 匹配的 PHTML 模板,包含所有字段类型
  • 支持模块文件(registration.php、module.xml、composer.json)
  • 目录结构概览

需要了解各部分如何配合时,请阅读此文件作为参考。

组件/字段架构(从项目读取,并提供内置备用方案)

Hyvä CMS 架构取决于项目版本。**优先使用已安装的 Hyvä Commerce 软件包中的实时架构:**如果 vendor/hyva-themes/commerce-module-cms/src/liveview-editor/etc/hyva_cms/jsonschema/ 存在,请直接读取其中的 JSON 文件(无需解释器),以获取本项目权威的组件和字段声明:

  • component-declaration.json — 有效的组件级属性
  • component-field-declaration.json — 字段声明属性、字段类型和验证属性

只有在未安装 Hyvä Commerce 时,才使用内置的 references/component-schema.md 作为备用方案。验证组件结构或遇到架构验证错误时,请读取上述任一来源。

内置的 references/component-schema.md 是备用快照。Hyvä CMS 更新后,如需刷新该文件,请读取上文所述的 jsonschema/*.json 文件并重新生成——无需辅助脚本。

references/field-types.md

所有受支持字段类型的完整参考,包括:

  • 字段配置语法
  • 所有可用字段类型及示例
  • 验证属性
  • 条件可见性(show_if/hide_if)
  • 常见模式的字段预设

生成字段配置时,请阅读此文件。

references/variant-support.md

实现模板变体的指南,包括:

  • 变体模板的目录结构
  • components.json 中的变体字段配置
  • 模板实现模式
  • 常见变体模式和最佳实践

当用户想为组件提供多种布局选项时,请阅读此文件。

references/troubleshooting.md

常见问题的解决方案,包括:

  • 架构验证错误
  • 组件在编辑器中不可见
  • 模板未渲染
  • 实时编辑器无法工作
  • 图像显示问题
  • 依赖技能不可用时的回退方案

创建或测试组件时遇到错误,请阅读此文件。

assets/templates/component/template.phtml.tpl

CMS 组件的 PHTML 基本结构。

占位符:

  • {{CONTENT_FIELDS}} - PHP 变量声明
  • {{TEMPLATE_BODY}} - HTML 模板内容

重要指南

  1. 始终在根元素和每个可编辑字段元素上使用 getEditorAttrs()
  2. 模板中绝不要使用 <script> 标签——通过 alpine:init 事件使用 Alpine.js
  3. 使用适当的转义方法转义所有用户内容
  4. 使用有意义的默认值,提升商家的使用体验
  5. 通过 include 引入设计/高级部分,以保持一致性
  6. 验证组件名称为 snake_case,且仅包含小写字母、数字和下划线
  7. 重要:使用 default_value 键,而不是 default——默认值的正确 JSON 键为 default_value(带下划线),而不是 default。示例:"default_value": "My Title" ✅,不要使用 "default": "My Title" ❌
  8. 重要:children 是根级属性,不是字段类型——绝不要在 content/design/advanced 中使用 "type": "children"。请在组件根级声明 children。在模板中通过 $block->getData('children') 访问。
  9. 重要:验证规则应放在 attributes 中,而不是作为直接属性——使用 "attributes": {"required": true} ✅,不要使用 "required": true ❌。所有 HTML5 验证属性(required、minlength、maxlength、pattern、min、max)都必须放在 attributes 对象中。

评论

支持 Markdown