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 エディターインターフェースに、組み込みフィールドタイプ(テキスト、テキストエリア、セレクトなど)を拡張した専用の入力コントロールを追加できます。
カスタムフィールドには 2 種類あります:
- 基本カスタムフィールドタイプ:データを直接入力するカスタム入力コントロール(日付範囲、カラーピッカー、カスタムバリデーションなど)
- フィールドハンドラー:複雑な操作に対応した拡張 UI(画像付き商品セレクター、検索可能なドロップダウン、リンク設定モーダルなど)
コマンドの実行: 開発環境内で実行する必要があるコマンド(例: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_CmsLiveviewEditorが含まれているcomposer.jsonにhyva-themes/commerce-module-cmsが含まれている
不足している依存関係があれば追加します。
ステップ 2:フィールドタイプの詳細
カスタムフィールドタイプに関する情報を収集します:
- フィールドタイプ名(
date_range、product_selector、color_pickerなどの小文字の識別子) - 目的(どのようなデータを収集するか)
- UI パターン:
- 基本フィールド:バリデーション付きのシンプルな入力(日付ピッカー、パターン入力、拡張テキストフィールド)
- インラインハンドラー:フィールド領域内の拡張コントロール(検索可能なドロップダウン、カラーピッカー)
- モーダルハンドラー:複雑な選択用の個別ダイアログ(商品セレクター、リンクビルダー、メディアギャラリー)
- データ構造(単純な文字列、JSON オブジェクト、配列など)
- バリデーション要件(パターン、必須、カスタムルールなど)
ステップ 3:実装パターンの選択
ステップ 2 で特定した UI パターンに基づいて選択します:
パターン A:基本カスタムフィールドタイプ
カスタム HTML5 バリデーションや専用の入力コントロールを使うシンプルな入力向けです:
- フィールド用テンプレートファイル 1 つ
- 個別のハンドラーモーダルなし
- 例:日付範囲セレクター、カスタムパターンバリデーション、スライダー入力
パターン B:インラインフィールドハンドラー
フィールド領域内に表示される拡張コントロール向けです:
- Alpine.js コンポーネントを含むテンプレートファイル 1 つ
- 個別のハンドラーモーダルなし
- 例:検索可能なセレクトドロップダウン、スウォッチ付きカラーピッカー
パターン 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を使ったエラー状態の処理- 重要: フィールド値には null 合体演算子を使用します:
$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クラス(静的なflexではありません)を持つ<dialog>要素 - フィールドテンプレートからの初期化イベントをリッスンする
- 選択 UI(検索、フィルター、グリッドなど)を実装する
- 保存時に
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
3 種類すべてのカスタムフィールドタイプの実装パターンです。
- 基本的なカスタムフィールドタイプ(シンプルな入力)
- インラインフィールドハンドラー(拡張コントロール)
- モーダルベースのフィールドハンドラー(ダイアログでの選択)
各パターンには、以下が含まれます。
- 完全なコード例
- 各パターンを使用する場面
- 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 コンポーネントを使用する場合は、入力フィールドをコンポーネントの外に配置し、バニラ JS で更新してください
- 組み込みの例:実績のあるパターンについては、
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を使用(デフォルト):Product、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 View Model:UI 要素に使用します
/** @var Icons $icons */ $icons = $viewModels->require(Icons::class); <?= /** @noEscape */ $icons->trashHtml('', 22, 22) ?> -
FieldTypes View Model:属性のフィルタリングに使用します
/** @var FieldTypes $fieldTypes */ $fieldTypes = $viewModels->require(FieldTypes::class); $filteredAttributes = $fieldTypes->getDefinedFieldAttributes($attributes); // Or for specific attributes: $filteredAttributes = $fieldTypes->getAttributesByKeys($attributes, ['required', 'data-required']); -
重要:Layout XML の referenceContainer:ハンドラーモーダルには必ず
before.body.endコンテナを使用してください- ✅ 正しい例:
<referenceContainer name="before.body.end"> - ❌ 誤った例:
<referenceContainer name="content"> before.body.endコンテナを使うと、ハンドラーモーダルがページの body の末尾に読み込まれます。これは、Alpine.js の初期化とモーダル機能を正常に動作させるために必要です
- ✅ 正しい例:
-
重要:フィールド値の型の扱い:フィールド値には型キャストを絶対に使用せず、常に null 合体演算子を使用してください
- ✅ 正しい例:
$fieldValue = $block->getData('value') ?? ''; - ❌ 誤った例:
$fieldValue = (string) $block->getData('value'); - 値が
nullの場合、型キャスト(string)は失敗し、PHP エラーが発生します - 文字列値には
?? ''、配列値には?? []、またはデータ型に適したデフォルト値を使用してください - 参照先:
?? []パターンを使用しているcategory.phtmlなどの組み込みフィールドタイプを確認してください
- 重要:ダイアログモーダルのクラス:ハンドラーモーダルには、静的な
flexクラスではなくopen: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; } } } // hidden input に出力する場合は、必ず JSON 文字列にする $fieldValueJson = is_array($fieldValue) ? json_encode($fieldValue) : $fieldValue; - ❌ 誤り:型を確認せずに
json_decode($fieldValue)を使用する(値がすでに配列の場合に失敗します) - ❌ 誤り:JSON エンコードせずに配列を
value属性で直接使用する