Skip to content

feat: 店舗設定に構造化データ拡張項目を追加 (sameAs/foundingDate 等) (#6147) - #6957

Draft
ttokoro20240902 wants to merge 7 commits into
4.4from
feature/issue-6147-baseinfo-schema
Draft

feat: 店舗設定に構造化データ拡張項目を追加 (sameAs/foundingDate 等) (#6147)#6957
ttokoro20240902 wants to merge 7 commits into
4.4from
feature/issue-6147-baseinfo-schema

Conversation

@ttokoro20240902

@ttokoro20240902 ttokoro20240902 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

概要 (Refs #6147, #6136)

店舗設定(BaseInfo)を拡張し、Organization / WebSite の構造化データに以下を出力できるようにします。

スカラー項目

  • Organization.sameAs(SNS等の公式URL・複数=改行区切り)
  • Organization.foundingDate(稼働開始日)
  • Organization.numberOfEmployees(従業員数, QuantitativeValue)
  • Organization.image(サイト代表画像URL)
  • WebSite.copyrightYear(著作権表示の開始年)

営業時間

  • Organization.openingHoursSpecification(曜日×開店/閉店時刻・複数行)
    • 既存の business_hour(自由テキスト・Help表示用)とは別に、構造化データ用の曜日/時刻を保持

依存 / 順序(Stacked PR)

変更内容

  • BaseInfo にスカラー5カラム追加 + OpeningHours(dtb_opening_hours) を OneToMany で追加
  • 店舗設定「基本設定」に入力欄(スカラー各種 + 営業時間の CollectionType 動的行UI)/翻訳 ja,en
  • SiteStructuredDataService に各項目を反映(値が空の任意プロパティは出力しない
  • マイグレーション Version20260722000000(BaseInfo列)/ Version20260722010000(dtb_opening_hours, Schema APIで両DB対応)
  • SiteStructuredDataServiceTest に各項目のテストを追加

テスト

  • ローカル: php-cs-fixer / PHPStan(level6) / PHPUnit(15 tests 38 assertions) / Rector すべて green
  • 実機: 管理画面フォームの表示・保存 → DB反映 → フロント JSON-LD 反映を確認(営業時間の行追加/削除・openingHoursSpecification 出力含む)

残作業(別軸, #6147

  • Google Shopping Feed 必須属性の充足(商品フィード側)

🤖 Generated with Claude Code

tao-s and others added 3 commits July 22, 2026 10:39
Organization / WebSite の JSON-LD をテンプレートの文字列補間で組み立てていたため、
店名等に " や </script> が含まれると構造化データが壊れる/XSS 経路になりうる問題があった
(CodeRabbit 指摘)。#6883 で導入済みの json_ld フィルタ(JSON_HEX_* でエスケープ)に揃える。

- SiteStructuredDataService を新設し WebSite / Organization の連想配列を組み立て
- TwigInitializeListener で site_json_ld を front グローバルに注入
- default_frame.twig は {{ site_json_ld|json_ld }} で出力
- 旧 Schema/organization.twig・website.twig を削除
- 値が空の任意プロパティは出力しない(ProductStructuredDataService と同方針)
- 都道府県未設定(Pref=null)でもフロントが 500 にならない(元 PR の不具合も解消)
- SiteStructuredDataServiceTest を追加

出力される JSON-LD は、値のある項目については従来と同一
(空の任意プロパティが省略される点のみ差分)。

Refs #6136 #6147

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Issue #6147#6139 で新設した SiteStructuredDataService に、店舗設定
(BaseInfo) から出力する構造化データ項目を拡充する。

- BaseInfo に same_as / founding_date / number_of_employees / copyright_year / site_image を追加
- 店舗設定「基本設定」に入力欄(ShopMasterType / shop_master.twig / 翻訳 ja,en)を追加
- SiteStructuredDataService で Organization の sameAs(配列)/ foundingDate /
  numberOfEmployees(QuantitativeValue)/ image、WebSite の copyrightYear を出力
- マイグレーション追加(Version20260722000000, TEXT型はMySQL/PostgreSQLで分岐)
- SiteStructuredDataServiceTest に各項目のテストを追加

値が空の任意プロパティは出力しない(#6139 の方針を踏襲)。

Refs #6136 #6147

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: eba39d47-ef7a-415f-8ba9-e58387275a92

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

店舗基本情報に構造化データ用の5項目を追加し、管理画面で編集できるようにしました。SiteStructuredDataService がJSON-LDを生成し、Twigグローバル経由でフロント画面へ出力します。

Changes

サイト情報の保存と管理画面

Layer / File(s) Summary
データ項目と管理画面
app/DoctrineMigrations/..., src/Eccube/Entity/BaseInfo.php, src/Eccube/Form/Type/Admin/ShopMasterType.php, src/Eccube/Resource/locale/*, src/Eccube/Resource/template/admin/...
same_asfounding_datenumber_of_employeescopyright_yearsite_imageを保存・入力するためのマイグレーション、ORM定義、フォーム、文言、テンプレートを追加。

JSON-LD生成と出力

Layer / File(s) Summary
JSON-LD生成サービス
src/Eccube/Service/SiteStructuredDataService.php, tests/Eccube/Tests/Service/SiteStructuredDataServiceTest.php
WebSiteおよびOrganizationのJSON-LD生成、任意値の省略、URL分割、住所・連絡先、設立日、従業員数などの処理とテストを追加。
Twigへの注入と画面出力
src/Eccube/EventListener/TwigInitializeListener.php, src/Eccube/Resource/template/default/default_frame.twig
生成したWebSite JSON-LDをsite_json_ldとしてTwigへ登録し、値が存在する場合にJSON-LDスクリプトを出力。

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant 管理画面
  participant BaseInfo
  participant SiteStructuredDataService
  participant Twig
  管理画面->>BaseInfo: 構造化データ項目を保存
  Twig->>SiteStructuredDataService: WebSite JSON-LDを生成
  SiteStructuredDataService->>BaseInfo: 店舗基本情報を参照
  SiteStructuredDataService-->>Twig: site_json_ldを返却
  Twig-->>Twig: JSON-LD scriptをレンダリング
Loading

Possibly related PRs

  • EC-CUBE/ec-cube#6139: site_json_ld、Twig出力、構造化データサービスの実装が重複します。
  • EC-CUBE/ec-cube#6953: BaseInfoとショップマスター管理フォームの変更が重複します。

Suggested labels: enhancement

Suggested reviewers: nanasess, dotani1111

Poem

ぴょんと項目を五つ追加
JSON-LDが巣から出発
店の名や住所を包んで
Twigの空へ舞い上がる
うさぎも満足、月明かり crescent

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 店舗設定への構造化データ拡張項目追加という変更内容を的確に要約しており、PRの主旨と一致しています。
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/issue-6147-baseinfo-schema

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ttokoro20240902

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/Eccube/Form/Type/Admin/ShopMasterType.php (1)

160-168: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

number_of_employees に上限を設ける値域チェックが必要

number_of_employees は数値のみを許容する Assert\Regex のみで、上限がありません。DBカラムは INT(migration参照)のため、桁数の大きい値を入力すると保存時にDBエラーとなる可能性があります。同ファイル内の copyright_yearbasic_point_ratepoint_conversion_rate は Range 制約で値域を制限しているため、同様の対応を推奨します。

♻️ 提案する修正
             ->add('number_of_employees', IntegerType::class, [
                 'required' => false,
                 'constraints' => [
                     new Assert\Regex([
                         'pattern' => "/^\d+$/u",
                         'message' => 'form_error.numeric_only',
                     ]),
+                    new Assert\Range([
+                        'min' => 0,
+                        'max' => 2147483647,
+                    ]),
                 ],
             ])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Eccube/Form/Type/Admin/ShopMasterType.php` around lines 160 - 168, Update
the number_of_employees constraints in ShopMasterType to include an Assert\Range
upper bound matching the database INT column, while preserving the existing
numeric-only validation. Follow the established Range constraint pattern used by
copyright_year, basic_point_rate, and point_conversion_rate, including an
appropriate validation message.
src/Eccube/EventListener/TwigInitializeListener.php (1)

181-181: 🚀 Performance & Scalability | 🔵 Trivial

構造化データの生成はリクエスト毎に行われる点に留意

createWebSiteJsonLd はフロント全ページのリクエストごとに呼び出され、キャッシュされません。現状は軽量な処理(配列構築とルート生成数回)のため大きな懸念はありませんが、将来的に負荷が問題になる場合は、BaseInfo の更新時のみ再計算するキャッシュ戦略(例: タグ付きキャッシュや BaseInfo 更新イベントでの無効化)を検討してください。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Eccube/EventListener/TwigInitializeListener.php` at line 181, Review the
site_json_ld initialization in TwigInitializeListener and add a cache for
createWebSiteJsonLd keyed to the current BaseInfo state, with invalidation when
BaseInfo is updated; ensure subsequent requests reuse the cached structured data
until that update occurs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/DoctrineMigrations/Version20260722000000.php`:
- Around line 35-73: Remove the Version20260722000000 migration because it only
adds nullable columns already represented by BaseInfo entity attributes and
should be applied through schema:update. Delete both up() and down() handling
for same_as, founding_date, number_of_employees, copyright_year, and site_image;
retain the migration only if a documented production-upgrade requirement makes
it necessary, with that exception explicitly recorded.

---

Nitpick comments:
In `@src/Eccube/EventListener/TwigInitializeListener.php`:
- Line 181: Review the site_json_ld initialization in TwigInitializeListener and
add a cache for createWebSiteJsonLd keyed to the current BaseInfo state, with
invalidation when BaseInfo is updated; ensure subsequent requests reuse the
cached structured data until that update occurs.

In `@src/Eccube/Form/Type/Admin/ShopMasterType.php`:
- Around line 160-168: Update the number_of_employees constraints in
ShopMasterType to include an Assert\Range upper bound matching the database INT
column, while preserving the existing numeric-only validation. Follow the
established Range constraint pattern used by copyright_year, basic_point_rate,
and point_conversion_rate, including an appropriate validation message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6a3143e4-2c95-413a-bc02-0809e40c0501

📥 Commits

Reviewing files that changed from the base of the PR and between b978d3a and b9d9b26.

📒 Files selected for processing (10)
  • app/DoctrineMigrations/Version20260722000000.php
  • src/Eccube/Entity/BaseInfo.php
  • src/Eccube/EventListener/TwigInitializeListener.php
  • src/Eccube/Form/Type/Admin/ShopMasterType.php
  • src/Eccube/Resource/locale/messages.en.yaml
  • src/Eccube/Resource/locale/messages.ja.yaml
  • src/Eccube/Resource/template/admin/Setting/Shop/shop_master.twig
  • src/Eccube/Resource/template/default/default_frame.twig
  • src/Eccube/Service/SiteStructuredDataService.php
  • tests/Eccube/Tests/Service/SiteStructuredDataServiceTest.php

Comment thread app/DoctrineMigrations/Version20260722000000.php Outdated
@ttokoro20240902
ttokoro20240902 force-pushed the feature/issue-6147-baseinfo-schema branch from 247de17 to b031ca9 Compare July 22, 2026 05:53
Issue #6147。営業時間を構造化データ(Organization.openingHoursSpecification)
として出力できるようにする。既存 business_hour(自由テキスト・Help表示用)とは
別に、曜日×開店/閉店時刻を構造化して保持する。

- OpeningHours エンティティ(dtb_opening_hours)を新設し BaseInfo に OneToMany を追加
- 店舗設定「基本設定」に CollectionType の営業時間入力欄(動的な行追加/削除UI)を追加
- OpeningHoursType(曜日=複数選択, opens/closes=TimeType)・prototype テンプレート
- SiteStructuredDataService で openingHoursSpecification(配列)を出力
  (曜日/時刻がいずれも無いエントリは出力しない)
- バリデーション: 開店<閉店・曜日必須・開店/閉店ペア必須(OpeningHours)、
  同一曜日の時間帯重複禁止(ShopMasterType)。BaseInfo の OneToMany に Assert\Valid でカスケード
- 削除ボタンは既存の collection UI に合わせ fa-close アイコン化、時刻欄クリックで showPicker
- マイグレーション Version20260722010000(Schema APIで両DB対応)
- 翻訳(ja/en, ラベルは messages・エラーは validators)・テスト追加

Refs #6136 #6147

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ttokoro20240902
ttokoro20240902 force-pushed the feature/issue-6147-baseinfo-schema branch from b031ca9 to 4e3a5c0 Compare July 22, 2026 06:22
ttokoro20240902 and others added 2 commits July 23, 2026 09:29
- same_as: 改行区切り各行を Assert\Url で検証(site_image と対称化)
- founding_date: LessThanOrEqual('today') で未来日を禁止
- number_of_employees: Regex を Assert\PositiveOrZero に置換(非負整数)
- 営業時間の重複エラーを該当行に表示(overlap の atPath を closes に変更)
- ShopMasterTypeTest にスカラー5項目のUT 14件を追加(計32件)
- admin-basicinfo.spec.ts に営業時間の動的行UI・保存往復・JSON-LD反映・重複エラーのE2Eを追加(冪等)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- number_of_employees に Assert\LessThanOrEqual(2147483647) を追加(INT桁あふれ防止)
  併せて上限のUT 2件を追加(境界2147483647は有効・2147483648は無効)
- Version20260722000000(BaseInfo単純nullableカラム追加のみ)を削除
  Entity属性+schema:update で反映されるためガイドライン準拠(dtb_opening_hours
  作成の Version20260722010000 はテーブル新設のため維持)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ttokoro20240902

ttokoro20240902 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

CodeRabbit のレビュー指摘に対応しました(commit 4e97130 / c4e64cb)。

  • number_of_employees の値域上限: Assert\LessThanOrEqual(2147483647)(INT 上限)を追加し、桁あふれによる保存時 DB エラーを防止しました。上限の単体テスト 2 件(境界 2147483647 = 有効 / 2147483648 = 無効)も追加しています。
    ※ 併せて元の Assert\RegexAssert\PositiveOrZero に置換済みで、負値も拒否します。

  • ② JSON-LD がリクエスト毎に生成される点(TwigInitializeListener:181: 今回は見送りました。ご指摘のとおり処理は軽量(連想配列の構築とルート生成が数回)で、CodeRabbit の評価も「現状は大きな懸念なし」のためです。将来的に負荷が問題化した場合は、BaseInfo 更新イベントでのタグ付きキャッシュ無効化を検討します。

  • ③ 単純カラム追加のための migration: 該当インラインスレッドのとおり Version20260722000000 を削除しました(dtb_opening_hours 新設の Version20260722010000 は維持)。

なお、本 PR には別途、追加項目のバリデーション強化(same_as の各行 URL 検証、founding_date の未来日禁止、営業時間の重複エラー表示)と、FormType 単体テスト・管理画面 E2E を追加済みです。

@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.07143% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.27%. Comparing base (b1c2436) to head (026a317).

Files with missing lines Patch % Lines
src/Eccube/Entity/OpeningHours.php 67.56% 12 Missing ⚠️
src/Eccube/Service/SiteStructuredDataService.php 93.45% 7 Missing ⚠️
src/Eccube/Form/Type/Admin/ShopMasterType.php 96.70% 3 Missing ⚠️
src/Eccube/Entity/BaseInfo.php 91.30% 2 Missing ⚠️
src/Eccube/Repository/OpeningHoursRepository.php 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              4.4    #6957      +/-   ##
==========================================
+ Coverage   77.14%   77.27%   +0.13%     
==========================================
  Files         546      550       +4     
  Lines       27133    27413     +280     
==========================================
+ Hits        20932    21184     +252     
- Misses       6201     6229      +28     
Flag Coverage Δ
Unit 77.27% <91.07%> (+0.13%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants