WordPress CMS統合ガイド¶
このドキュメントは、Storefront、Vendure、および WordPress CMSの統合方法、アーキテクチャ、実装パターンを説明します。
概要¶
このプロジェクトでは、WordPressをHeadless CMSとして使用し、以下のコンテンツを管理します:
- ナビゲーションメニュー: ヘッダー・フッターの動的なリンク構造
- お知らせ(Announcements): サイト全体のお知らせ、重要なお知らせの固定表示
- キャンペーン(Campaigns): プロモーションキャンペーン情報
- メインバナー(ホームヒーロー): ホームページのヒーローセクション用バナー
- 店舗設定: ロゴ、コピーライト、コンタクト情報、ホーム下部バナー、SNSリンクなど
ストア設定内のSNSリンク¶
ストア設定(site-settings)の socialList は、WordPress の ACF
Options で管理し、Vendure の wordPressStoreSettings
経由で Storefront に渡します。
socialListが 未取得 (null/undefined) の場合でも、Storefront は推測で外部 SNS を表示せず、有効な実 URL がある行だけ を表示するsocialListが 空配列、または登録行がすべて無効・URL未設定で 有効リンク 0 件 の場合は、Storefront は SNS を表示しないexample.com/example.org/example.net系のプレースホルダー URL は、本番導線として扱わず非表示にするiconUrlは未知の SNS 種別またはcustom用のアイコンとして使い、既知のLINE / Instagram / YouTubeは固定アイコンを優先する
ブランド表示と導線の正本¶
ブランドの名称・説明文・画像と、その配下のメニュー導線は WordPress ではなく、Vendure の
brands 親 collection 配下の collection tree を正本として管理します。
- まず Vendure 側で
brandFacet と各 FacetValue を用意する - 1 ブランド 1 child collection を作成し、
facet-value-filterでbrand=<facet value>を設定する - 必要に応じてブランド配下に
業務用商品/店販用商品/セット商品/販促品/キャンペーン商品などの menu collection を追加し、親 filter 継承 + 追加 Facet 条件で絞り込む - ブランド / menu collection の
slugは Storefront 導線と plugin 判定に使う安定キーとして管理する name/description/featuredAsset/assetsを Storefront のブランド表示やメニュー導線に利用する- ホームの「ご契約ブランド一覧」と商品一覧上部のブランド紹介は、 当該ブランド collection に可視商品が1件以上ある場合のみ表示する
visibleCollectionIdsは collection の許可条件としては使うが、表示の最終判定は 可視商品件数で行う- Storefront のブランドメニューは Vendure Shop API の
collectionsからbrandsroot 配下の collection tree を作り、その後visibleCollectionIdsで顧客別に再帰フィルタする。isPrivate=trueの collection は Shop API の tree 取得時点で除外されるため、商品可視性ルールとは別条件として扱う - ブランド表示判定は Storefront で span / structured log / breadcrumb を記録し、 判定対象数・表示件数・所要時間を観測できるようにする
- Brand のデータ真実は
brandFacetValue とし、Collection はそのページ / 導線表現として扱う - 価格・配送・同意・契約条件は WordPress ではなく Vendure の Facet / custom field で管理する
- WordPress の
site-settingsはブランド情報を保持せず、ロゴ・連絡先・ホーム下部バナー・SNSなどの共通設定だけを管理する
ブランドメニュー表示条件 matrix¶
| Vendure collection の状態 | Shop API collections の tree |
顧客別 visibleCollectionIds / 可視商品 |
Storefront ブランドメニュー | 運用診断 /diagnostics |
対応方針 |
|---|---|---|---|---|---|
brands 配下で isPrivate=false |
tree に含まれる | 自身、または子孫に可視商品がある | 表示される | 警告なし | 正常。 |
brands 配下で isPrivate=false |
tree に含まれる | 自身にも子孫にも可視商品がない | 表示されない | 警告なし | 商品紐付け、可視性ルール、検索 index を確認する。 |
brands 配下で isPrivate=true |
tree から除外される | 可視商品がある | 表示されない | private-brand-collections が warning |
意図的に隠す collection でなければ非公開を解除する。 |
brands 配下で isPrivate=true |
tree から除外される | 可視商品がない | 表示されない | private-brand-collections が warning |
非公開解除後、可視性ルールと商品紐付けも確認する。 |
brands 配下の祖先 collection が isPrivate=true |
private 祖先が tree から除外され、子孫も brands tree に接続できない |
子孫に可視商品がある場合でも menu tree に載らないことがある | 表示されない | private 祖先が warning | 親を公開するか、導線に出す階層構造を組み直す。 |
brands 配下ではない collection |
ブランド menu の対象外 | 任意 | ブランドメニューには表示しない | 警告なし | キャンペーン・商品タイプなど別 root の導線で扱う。 |
アーキテクチャ¶
StorefrontはWordPressへ直接アクセスするのではなく、Vendureの
CmsIntegrationPlugin
をプロキシとして経由します。これにより、認証情報の秘匿、APIの一元化、およびデータの正規化が可能になります。
┌─────────────────┐
│ WordPress CMS │
│ (Headless) │
│ │
│ - Menu Items │
│ - Custom Posts │
│ - ACF Settings │
└────────┬────────┘
│ WPGraphQL (Internal)
│
▼
┌─────────────────┐
│ Vendure Server │
│ (CMS Plugin) │
│ │
│ - Data Proxy │
│ - Normalization │
│ - Resolvers │
└────────┬────────┘
│ Shop API (Public)
│
▼
┌─────────────────┐
│ Storefront │
│ (Vite) │
│ │
│ - Route Loaders │
│ - Components │
│ - Display │
└─────────────────┘
1. サイト外枠(枠組み)¶
- 担当: Storefront
- 対象: ヘッダー、フッター、グローバルナビゲーション
- 管理: WordPressの「メニュー」機能(位置:
PRIMARY,FOOTER)で管理。VendureがこれをheaderMenu,footerMenuとして公開し、StorefrontがReactコンポーネントとして描画します。
2. メインコンテンツ(中身)¶
- 担当: WordPress(本文・埋め込みコンテンツ)/ Storefront(レイアウト・ページ構成)
- 対象: 固定ページ、お知らせ詳細、キャンペーン詳細(Storefrontの該当セクション)
- 実装: WordPressが生成するHTMLをVendure経由で取得し、Storefrontの
ContentProcessorで加工して描画します。 - 本文中リンクの扱い:
content-processor.tsのnormalizeInternalHrefは WordPress オリジンと同一ホストの絶対URLだけを Storefront パスへ寄せ、 ホストが異なる外部URL (parsed.hostname !== base.hostname) はそのまま保持します。 メニュー (normalizeCmsUrl) / コンポーネントリンク (classifyLinkHref) / アセットURL (normalizeCmsAssetUrl) も同様に、外部ドメインを上書きしない方針で統一しています。
WordPress stylesheet の適用境界¶
WordPress の block markup を styleMode = wordpress_theme で表示する場合、Storefront
は WordPress core の wp-block-library / wp-block-library-theme を CMS 表示境界で読み込みます。
これは Vimeo などの埋め込みに付く .wp-embed-aspect-16-9 /
.wp-has-aspect-ratio を Storefront 側で個別再実装せず、WordPress の表示作法を継承するためです。
- WordPress stylesheet の読み込み責務は Storefront の
CmsAssetLoaderに限定する。 - Vendure Server / Vendure Dashboard / React Dashboard 側の CSS や component
には WordPress core CSS、
.editor-styles-wrapper、.wp-content前提の補正を持ち込まない。 - 商品リッチ説明では
styleMode = wordpress_themeのときだけ WordPress stylesheet を読み込み、styleMode = storefront_themeでは Storefront 側 typography を使う。 - WordPress core stylesheet はブラウザ上では global stylesheet として効くため、同じ
Storefront ページ内での影響を避けたい本文は
storefront_themeとして管理する。 - 具体的な商品リッチ説明の選択順、collection 階層優先、sanitizer 経路は
product-detail-product-scope.mdを正本にする。
実装パターン¶
正規化処理の共通化¶
WordPress特有のデータ構造をRitsubiドメインのクリーンなデータに変換するため、正規化処理を重層的に行います。
- Shared Utilities (@ritsubi/utils):
slugify/normalizeCmsUrl/cmsUrlToStorefrontPathといった純粋な変換関数を提供します。バックエンドとフロントエンドの両方で使用されます。 normalizeCmsUrl(url, { cmsHosts }): リンク描画用。相対パス (/page/...等) は投稿系ルートへ寄せる。 絶対URLはcmsHosts(= CMS 自身の host) に一致した場合だけ Storefront path へ寄せ、 それ以外のドメインを含む絶対URL(外部リンク)は 一切加工せず保持する。 これにより「編集者が CMS ドメインで書いた内部ナビ(例: ヘッダー)」は相対パス化されて SPA 遷移を維持しつつ、forms.gle等の 外部URLは origin を上書きされず正しく機能する。cmsHostsは host の正本である WordPress エンドポイント (backend=config.options.endpoint/ Storefront=VITE_PUBLIC_WORDPRESS_URL、getCmsHosts()) から導出する。cmsUrlToStorefrontPath: 入力が CMS 自身の内部URLであることが確定している用途 (お知らせ permalink、WP ページ lookup キー) でのみ使用し、cmsHosts判定なしで絶対URLでも origin を落としてパス化する。- Vendure Plugin (Backend): WPGraphQLの冗長なネスト(
nodes { ... }等)を排除し、フロントエンドが扱いやすいフラットな型へ正規化します。 - Storefront (Frontend):
Vendureから受け取ったデータを、UIコンポーネントのProps(
NavigationItem等)に最終マッピングします。
GraphQLクライアント¶
Storefront からの CMS リクエストは apps/storefront/src/lib/cms/client.ts の
fetchCmsGraphQL を使用します。この関数は Vendure の Shop
API を唯一の取得先 とし、Storefront から WordPress へ直接アクセスしません。
- Storefront の責務は「Vendure schema 上の CMS query を呼ぶ」ことに限定する
- 上流 CMS が WordPress であることは Vendure plugin 側の実装詳細として閉じ込める
apps/storefront/src/lib/cms/content/*は、現時点では WordPress 由来コンテンツの型・整形責務を表すディレクトリ名であり、通信先を意味しない
Storefront -> Vendure /shop-api -> CmsIntegrationPlugin -> WordPress WPGraphQL
メニュー同期 (Menus)¶
- WordPress側: 「外観」→「メニュー」で作成。位置を「Header」または「Footer」に設定。
- Vendure側:
CmsIntegrationPluginがheaderMenu,footerMenuクエリを提供。menu の WP fetch は plugin 内の fetch cache(既定 TTL 300 秒)を使う。 即時反映よりも runtime 安定性(slow CMS での 5s timeout 回避)を優先する設計で、withPersistentSnapshotは毎回ライブ取得し、上流障害時のみ snapshot へフォールバックする (参照:wordpress.service.tsfetchMenuFromWordPressのコメント)。このため WordPress 側の メニュー編集が Shop API へ反映されるまで 最大で plugin fetch cache TTL(約 5 分)かかる。 - Storefront側:
fetchMenus()で両方のメニューを一括取得し、Header/Footerコンポーネントに反映。 - リンクの正規化(host 判定): 各メニュー項目の URL は host で内部 / 外部を判定する。
CMS 自ドメイン(WordPress エンドポイント host)の絶対URLと相対パスは Storefront ルートへ
寄せて SPA 遷移を維持し、それ以外の外部ドメイン(
forms.gle等)は origin を保持して そのまま使う(openInNewTabで別タブ)。詳細は後述の「メニューリンクのドメイン処理」節を参照。 - 編集者向け運用: 内部ページへの導線は相対パス(
/products//pages/...)または page link で設定することを推奨(環境差や余計なリダイレクトを避けられる)。外部サイトへは フルURLを設定し、必要に応じて「新しいタブで開く」を有効化する。
即時反映 WP-to-Storefront Webhook¶
WordPress の管理画面で保存しただけで storefront へ即時反映する。Cloudflare 管理画面や CLI 操作は不要。
- 発火点:
apps/wordpress-cms/plugins/ritsubi-ec-plugin/ritsubi-acf-vendure-bridge.php wp_update_nav_menu/wp_create_nav_menu/wp_delete_nav_menu→event: "menu.updated"transition_post_status(publish 状態の出入りのみ) →event: "<type>.updated"page→page.updatedannouncement/word_press_announcement→announcement.updatedcampaign/word_press_campaign→campaign.updatedproduct_detail→product.updated(storefrontwordPressProductDetailのproductsタグを invalidate するため、page.updatedではなく専用イベント)
acf/save_post(post_id が非数値の文字列、user/term/comment/widget/block meta 以外) →event: "settings.updated"- 対象は ACF Options ページ全般(ストア設定 / メインバナー等)。
post_id を
"options"既定で使うものも、明示指定した custom post_id も一様に捕捉する。
- 対象は ACF Options ページ全般(ストア設定 / メインバナー等)。
post_id を
- 送信先:
STOREFRONT_REVALIDATE_URL(未設定ならSTOREFRONT_URL+/api/revalidate/cms) - 認証:
x-cms-revalidate-secretヘッダ。値は storefront と共有のCMS_REVALIDATE_SECRET(b2b-ecommerce/{env}/shared配下)。 - 失敗時挙動: 非 blocking POST で管理画面の保存はブロックしない。失敗は
error_logに残す。
各 event がどのタグを invalidate するかは
apps/storefront/src/lib/cms/content/cache-tags.ts の EVENT_PREFIX_TO_TAGS を参照。
webhook は storefront 側のデータキャッシュタグを invalidate するが、上流 WordPress の
鮮度は Vendure plugin 側の fetch cache に依存する。
[!NOTE] menu を含むすべての CMS fetch は Vendure 側の
WORDPRESS_FETCH_CACHE_TTL_SECONDS(既定 300s)の影響を受ける(menu も bypass しない。 安定性優先の設計理由はwordpress.service.tsfetchMenuFromWordPressのコメント参照)。 したがって WordPress 編集の Shop API への反映は 最大で約 5 分 遅延し得る。即時反映が必要なら 該当 query への invalidate API 追加が follow-up となる。
設定¶
環境変数 (Vendure Server)¶
バックエンドがWordPressへアクセスするための設定です。
WORDPRESS_ENDPOINT: WordPressのベースURL(例:http://localhost:8181)WORDPRESS_GRAPHQL_TOKEN: WPGraphQLの認証が必要な場合のBearerトークン
環境変数 (Storefront)¶
VENDURE_BASE_URL: Vendure サーバーのベースURL(推奨・一本化)VITE_PUBLIC_VENDURE_BASE_URL: Storefront が参照する公開向け Vendure ベースURLVITE_PUBLIC_STOREFRONT_API_MOCKING:enabledに設定するとMSW経由でのモックが有効になります。
Storefront の CMS 通信に WORDPRESS_ENDPOINT は使用しません。これは
Vendure が上流 CMS へ接続するための設定 と、 /api/health/cms
における upstream 診断用途に限定します。
開発とテスト¶
テスト戦略¶
- バックエンドテスト:
packages/plugins/src/cms-integration/wordpress/*.spec.tsで、WordPressからの生データが正しく正規化され、GraphQLリゾルバーが機能することを検証します。 - フロントエンドテスト:
apps/storefront/src/lib/cms/content/*.test.tsで、VendureからのデータがUIコンポーネント向けに正しくマッピングされることを検証します。 - SNSリンク表示テスト:
Storefront 側で「未取得時非表示」「明示的に空のとき非表示」「プレースホルダーURLの除外」「未知SNSの
iconUrl表示」を検証します。
モックデータの扱い¶
ソースコード内にハードコードされた大規模なモックデータは廃止されました。開発時にモックが必要な場合は、apps/storefront/src/mocks/cms-handlers.ts
(MSW) にデータを定義してください。
トラブルシューティング¶
メニューが反映されない¶
- WordPress側でメニューが作成され、正しい「位置」(PRIMARY/FOOTER)に割り当てられているか確認してください。
- Vendureサーバーのログを確認し、WordPress APIへのリクエストが成功しているか(200 OK)を確認してください。
- GraphQL Playground (
/admin-api等) でquery { headerMenu { items { label } } }がデータを返すか直接確認してください。
メニューリンクのドメイン処理(内部 / 外部の判定)¶
メニューリンクは host で内部 / 外部を判定します。内部 host = CMS 自ドメイン + storefront 自身の canonical / alias で、いずれかに一致する絶対URLは相対パスへ寄せます。
- CMS 自ドメイン (
config.options.endpoint/VITE_PUBLIC_WORDPRESS_URLの host、 例:cms.ritsubi-platform.com) の絶対URLは 内部ナビとみなし、相対パスへ寄せます。 これによりヘッダー等の内部導線は SPA 遷移 (高速) を維持します。相対パス (/page/...) や page link (linkType: page) も同様に内部扱い(permalink / page uri はcmsUrlToStorefrontPath)。 - storefront 自身の canonical / alias (
storefrontConfig.siteUrlの host +VITE_PUBLIC_STOREFRONT_ALIAS_HOSTS、例:order.ritsubi-platform.com/medical.ritsubi.co.jp) の絶対URLも 内部扱いで相対化します。これは編集者が公開エイリアスの フルURL (https://medical.ritsubi.co.jp/pages/guides) をメニューに設定した場合に、 別オリジン遷移による cookie / セッションロスを避けるため(SSOT はgetStorefrontSelfHosts()/classifyLinkHrefと共通)。storefront 層の正規化が backend (Shop API) 由来の絶対URLも再正規化する。 - それ以外のドメイン (
forms.gle、提携サイト等) の絶対URLは 外部リンクとみなし、 origin を一切上書きせずそのまま使います。openInNewTabを設定すれば別タブで開きます。 - 注意: CMS 自ドメインの判定は環境ごとの WordPress エンドポイント host が正本です。
staging データに production の CMS ドメイン (
cms.ritsubi-platform.com) を直書きすると、 staging の host (cms-staging...) と一致せず外部リンク扱いになります。内部導線は相対パス推奨。
Storefront から WordPress に直接つながって見える¶
- Storefront には
apps/storefront/src/lib/cms/content/*というディレクトリ名が残っていますが、これは主に WordPress 由来コンテンツの型・整形責務を表します - 実際の通信入口は
apps/storefront/src/lib/cms/client.tsのfetchCmsGraphQLで、接続先は Vendureshop-apiです - 上流 CMS の疎通だけを見たい場合は
/api/health/cmsのupstreamCmsを確認し、通常導線の確認はvendureCmsを確認してください /api/health/cmsは Tier 3 診断用途 であり、継続的なプローブには使わないこと(Vendure・WordPress upstream へのファンアウトが発生するため)
ヘルスチェック・プローブの使い分け¶
エンドポイント早見表¶
| 用途 | エンドポイント | 備考 |
|---|---|---|
| 継続的プローブ(Tier 0 Safe Probe) | storefront /api/health/livestorefront /api/health/readystorefront /api/version |
上流ファンアウトなし。スケジュール自動実行に使う |
| CMS 診断(Tier 3 Diagnostic) | storefront /api/health/cms |
Vendure + WordPress upstream 両方を確認。手動診断時のみ使う |
継続的なスケジュールプローブは .github/workflows/scheduled-safe-probes.yml
が30分ごとに Tier 0 エンドポイント(Storefront・Vendure 両方)を確認します。
/api/health/cms をスケジュール監視の対象にしないでください。
各エンドポイントが答える問い¶
それぞれのエンドポイントは「異なる問い」に答えます。問いが違うため、用途も異なります。
| エンドポイント | 答える問い |
|---|---|
/api/health/live |
アプリのプロセスは生きているか? ─ プロセスがクラッシュしていないかを確認する最も軽量なチェック。 |
/api/health/ready |
リクエストを受け付けられる状態か? ─ 起動・初期化が完了しトラフィックを処理できるかを確認する。 |
/api/version |
デプロイ済みのバージョンは何か? ─ 期待するリリースがデプロイされているかを確認する。 |
/api/health/cms |
Vendure→WordPress の CMS 取得経路は健全か? ─ コンテンツが取得できない原因が Vendure 側にあるか WordPress upstream 側にあるかを切り分ける診断用チェック。 |
なぜ /api/health/live / /api/health/ready があるのに /api/health/cms が必要なのか¶
/api/health/live と /api/health/ready
はアプリ自身の状態(プロセス生存・初期化)しか確認しません。WordPress のコンテンツが取得できるかどうかは、アプリの「生存」とは独立した別の問題です。
たとえば、次の状況ではアプリは live かつ ready
であっても、コンテンツは表示されません:
- WordPress が停止または応答遅延している
- Vendure の
CmsIntegrationPluginの環境変数(WORDPRESS_ENDPOINTなど)が誤設定 - WordPress と Vendure 間のネットワーク経路に障害
こうした CMS 経路特有の問題を切り分けるために、/api/health/cms
が独立した診断エンドポイントとして存在します。
なぜ /api/health/cms を継続的プローブに使ってはいけないのか¶
/api/health/cms を呼ぶたびに Vendure と WordPress
upstream の両方へ実際にリクエストが飛びます(ファンアウト)。これを高頻度で実行すると:
- WordPress に不要な負荷がかかる
- Vendure のロードバランサーやヘルスチェック判定に誤影響を与えるおそれがある
- Tier 0 セーフプローブが担うシンプルな死活監視の目的と混在する
継続的な死活監視は Tier 0 エンドポイントで十分です。/api/health/cms
は障害調査など必要なときだけ手動で呼ぶものとして扱ってください。
vendureCms と upstreamCms の読み方¶
/api/health/cms
のレスポンスには2つのステータスフィールドが含まれます。これを使って障害箇所を絞り込めます。
| フィールド | 意味 | fail のとき疑う箇所 |
|---|---|---|
vendureCms.status |
Vendure(CmsIntegrationPlugin)側で CMS データを取得・整形できているか | Vendure の環境変数、プラグイン設定、Vendure→WordPress 内部経路 |
upstreamCms.status |
Vendure から WordPress(upstream)へ直接疎通できているか | WordPress 本体、WPGraphQL プラグイン、パーマリンク設定、ネットワーク・認証 |
解釈の目安:
vendureCms |
upstreamCms |
読み取り |
|---|---|---|
ok |
ok |
CMS 経路は正常 |
ok |
fail |
Vendure はキャッシュなどで応答できているが upstream に異常。早めに WordPress を確認する |
fail |
ok |
Vendure 内部の問題(プラグイン設定ミスなど)。upstream は生きている |
fail |
fail |
Vendure から WordPress への経路に全体的な問題がある |
ランブック: コンテンツが見えないがアプリは生きている¶
「お知らせ・バナー・メニューが表示されない、でも Storefront 自体は動いている」という状況での調査手順です。
ステップ 1: アプリ自体の生存を確認する¶
GET /api/health/live
GET /api/health/ready
両方 200 OK かつ status: ok
であれば、アプリは正常稼働中。問題は CMS 経路にあります。
ステップ 2: CMS 経路を診断する(手動・1回だけ)¶
GET /api/health/cms
レスポンスの vendureCms.status と upstreamCms.status
を確認し、上の表で障害箇所を絞り込みます。
production では公開面の診断情報を絞るため、
/api/health/cmsの詳細messageは返さず status-only です。詳細切り分けが必要な場合は、non-production 環境または runtime logs / Sentry / upstream 側の確認を併用してください。
ステップ 3A: upstreamCms.status が fail の場合 → WordPress を確認する
- WordPress 管理画面(
/wp-admin)にアクセスできるか確認 - WPGraphQL エンドポイント(
/graphql)が応答するか確認 - Vendure 側の
WORDPRESS_ENDPOINT環境変数が正しいか確認 - WordPress のパーマリンク設定(推奨: 数字ベース)を確認
ステップ 3B: vendureCms.status が fail の場合 → Vendure を確認する
- Vendure サーバーのログで
CmsIntegrationPlugin関連のエラーを確認 WORDPRESS_ENDPOINT/WORDPRESS_GRAPHQL_TOKENの環境変数を確認- Vendure Dashboard の GraphQL Playground で CMS クエリを直接実行して切り分け
ステップ 4: 通常導線に戻って確認する¶
問題が解消したら、/api/health/cms
ではなく実際の Storefront 画面(お知らせ一覧・バナーなど)で表示を確認してください。プローブが
ok でもキャッシュの問題でコンテンツが古い場合があります。
WordPress 管理画面のリソースピッカーでサジェストが表示されない¶
メインバナー等の設定画面で「商品選択」「コレクション選択」など Vendure リソースを選ぶピッカーフィールドのオートコンプリートが機能しない場合の診断手順です。
ピッカーの仕組み¶
WordPress 管理画面 (ACF フィールド)
└─ sku-field.js が Select2 として初期化
└─ POST /wp-admin/admin-ajax.php
{ action: ritsubi_product_search, nonce, term }
└─ PHP AJAX ハンドラ
└─ GET <VENDURE_BASE_URL>/cms-api/product-search
headers: x-ritsubi-signature, x-ritsubi-timestamp
└─ Vendure が DB 検索して JSON 返却
ピッカーの種類と対応エンドポイント:
| ACF フィールドタイプ | AJAX アクション | Vendure エンドポイント |
|---|---|---|
ritsubi_product_picker |
ritsubi_product_search |
/cms-api/product-search |
ritsubi_sku_picker |
ritsubi_sku_search |
/cms-api/sku-search |
ritsubi_collection_picker |
ritsubi_collection_search |
/cms-api/collection-search |
ritsubi_campaign_picker |
ritsubi_campaign_search |
/cms-api/campaign-search |
ritsubi_customer_picker |
ritsubi_customer_search |
/cms-api/customer-search |
ritsubi_customer_group_picker |
ritsubi_customer_group_search |
/cms-api/customer-group-search |
ritsubi_subject_set_picker |
ritsubi_subject_set_search |
/cms-api/subject-set-search |
[!IMPORTANT]
ritsubi_customer_searchのみ、CMS のedit_postsではなくmanage_options(Administrator 相当)が必要です。CMS 編集ロール(Editor/Author)が顧客 PII を引き出せないようにするためで、ピッカーが 403 を返す場合はロール設定を確認してください。あわせて customer picker のラベルはcustomerCode – nameのみで、emailAddressは返しません。
ステップ 1: ブラウザ DevTools で AJAX レスポンスを確認する¶
ブラウザの DevTools → Network タブを開き、admin-ajax.php への POST を探してレスポンスを確認します。
| レスポンス | 意味 | 次のステップ |
|---|---|---|
{"success":false,"data":{"code":"vendure_connection_not_configured"}} |
Vendure URL / トークン未設定 | ステップ 2A へ |
HTTP 502/503 + {"success":false,"data":{"code":"vendure_upstream_error"}} |
Vendure への接続失敗 | ステップ 2B へ |
HTTP 401 + {"success":false,"data":{"code":"vendure_upstream_error"}} |
HMAC 署名ミスマッチ | ステップ 2C へ |
[](空配列、HTTP 200) |
Vendure への接続は成功だが結果 0 件 | ステップ 2D へ |
[{value, label}, ...] なのに UI に表示されない |
JavaScript 初期化の問題 | ステップ 2E へ |
ステップ 2A: Vendure 接続設定を確認する¶
WordPress 管理画面の「サイト設定」オプションページ、または環境変数で以下が設定されているか確認します。
RITSUBI_VENDURE_BASE_URL 例: https://api.example.com(末尾スラッシュ不要)
RITSUBI_CMS_API_TOKEN Vendure 側の CMS_API_TOKEN と一致する値
PHP の優先順位: PHP定数 RITSUBI_* > 環境変数 RITSUBI_* > ACF オプション > WP オプション
ステップ 2B: ネットワーク疎通を確認する¶
WordPress サーバーから Vendure への HTTP 到達性を確認します。
# WordPress コンテナ内から
curl -I <VENDURE_BASE_URL>/cms-api/product-search?term=&take=1
タイムアウトは 5 秒です。Vendure の cold start がある場合は間に合わないことがあります。
ステップ 2C: HMAC 署名を確認する¶
Vendure の CMS_API_TOKEN と WordPress 側の RITSUBI_CMS_API_TOKEN が完全一致しているかを確認します(空白・改行の混入に注意)。Vendure サーバーのログで expired_signature / unauthorized が出ていれば、トークン不一致またはサーバー時刻のずれ(±5分以内が必須)が原因です。
ステップ 2D: Vendure の DB データを確認する¶
Vendure に有効な商品・コレクション等が存在するか確認します。
- 商品ピッカーは
variant.enabled = true AND product.enabled = trueの条件でフィルタされます - デフォルト言語のスラッグが存在しない商品は表示されません(翻訳 JOIN が NULL になるため)
packages/plugins/src/cms-integration/cms-api/cms-api.plugin.tsのproductSearchエンドポイントを参照
ステップ 2E: JavaScript 初期化を確認する¶
Select2 が初期化されていない場合、フィールドは素の <select> として表示されます。
- ブラウザコンソールで
window.RITSUBI_PICKER_FIELDが定義されているか確認 acfオブジェクトが存在するか確認(ACF Pro が有効か)- 他プラグインによる jQuery / Select2 の競合がないか確認
既知の不具合と修正(2026-05-08)¶
症状: ピッカーで文字を入力して検索すると結果が表示されない、または無効化済みの商品が混入する。
原因: packages/plugins/src/cms-integration/cms-api/cms-api.plugin.ts の skuSearch / productSearch / collectionSearch で、検索タームがある場合に query.where(...) を呼ぶと TypeORM QueryBuilder の仕様により事前設定の andWhere 条件(variant.enabled = true 等)が消えていた。
修正: query.where(...) → query.andWhere(...) に変更し、OR 条件を括弧でくくることですべての条件が正しく AND 結合されるよう修正(fix-cms-api-search-andwhere changeset)。
キャッシュのクリア¶
サジェスト結果は 5 分間 WordPress の Transient キャッシュに保存されます。設定変更後もすぐに反映されない場合は、以下で Transient をクリアしてください。
just wp-shell
wp transient delete --all