angeo / module-rich-data

angeo/module-rich-data

Magento 2 JSON-LD Rich Data for AI Engine Optimization. Injects spec-compliant Product, Organization, BreadcrumbList, FAQPage, and WebSite schema. Fixes the 'Product schema — JSON-LD structured data' signal in angeo/module-aeo-audit.

magento2-module Compatibility: 2.4.7-2.4.9 Code Quality: Fail Tests: Fail Security: Pass MIT

Are you the maintainer of angeo?

Packagento pulls angeo's Composer packages from the public registry so buyers can find them here.

Claim the namespace to take ownership, publish new releases directly, and start charging for premium versions.

Claim this namespace →

Angeo Rich Data — Magento 2

Packagist
License
PHP

Publishes one linked JSON-LD @graph per page so ChatGPT, Gemini, Perplexity and Google read your catalog as connected data instead of loose fragments. Fixes the schema signals in angeo/module-aeo-audit.


What this module fixes

AEO Audit signal Before After
Product schema — JSON-LD structured data FAIL / WARN PASS
Merchant policies — return & shipping schema FAIL PASS (when enabled & configured)
Product schema — AggregateRating WARN PASS (when reviews exist)
JSON-LD quality — BreadcrumbList WARN PASS (product, category and CMS pages)
FAQPage schema — AI answer eligibility WARN PASS (on pages you nominate)

Schema types injected

Schema Pages Key fields
Product Product pages name, description, image, sku, gtin/mpn, brand, offers, aggregateRating
ProductGroup Configurable product pages hasVariant, variesBy, productGroupID, per-variant offers
Organization All pages name, description, url, logo, sameAs, contactPoint
WebSite Homepage name, url, publisher
BreadcrumbList Product, category, CMS pages full trail
CollectionPage Category pages ItemList of the products on the current page
FAQPage Nominated CMS pages detected Q&A pairs

Every node has a stable @id, so offers.seller points at the same Organization on every page of the store rather than repeating a name string.


Installation

composer require angeo/module-rich-data
bin/magento setup:upgrade
bin/magento cache:flush

Upgrading from 1.x? Read UPGRADE.md — the markup shape changes and prices become tax-inclusive on VAT-display stores.


Output

{
  "@context": "https://schema.org",
  "@graph": [
    { "@type": "Organization", "@id": "https://shop.example/#organization", "name": "Example Store" },
    {
      "@type": "Product",
      "@id": "https://shop.example/alpine-jacket.html#product",
      "name": "Alpine Jacket",
      "offers": {
        "@type": "Offer",
        "price": "121.00",
        "priceCurrency": "EUR",
        "availability": "https://schema.org/InStock",
        "seller": { "@type": "Organization", "@id": "https://shop.example/#organization" }
      }
    },
    { "@type": "BreadcrumbList", "@id": "https://shop.example/alpine-jacket.html#breadcrumb" }
  ]
}

Prefer the 1.x layout of one script tag per schema? General → Output mode → One script tag per schema.


Configuration

Stores → Configuration → Angeo → Rich Data (JSON-LD)

General

  • Enabled
  • Output mode — @graph (default) or legacy one-tag-per-schema

Product schema

  • Enable, AggregateRating, SKU, Brand (attribute-driven)
  • GTIN / MPN (attribute-driven, off by default)
  • Item condition
  • Publish configurable products as ProductGroup, and a variant cap
  • priceValidUntil window in days (0 to omit)
  • In-stock / out-of-stock availability URLs

Prices follow the store's tax display setting, so the marked-up price matches the price on the page. Configurable, grouped and bundle products publish an AggregateOffer with lowPrice and highPrice.

Breadcrumb schema

  • Enable — runs on product, category and CMS pages

Merchant policies (return & shipping)

offers.hasMerchantReturnPolicy and offers.shippingDetails, required by Google and ChatGPT Shopping since January 2026. Off by default — enable only once your real return window, shipping rate and delivery times are known, because publishing wrong policy values can hurt eligibility.

Organization schema

Name, description, logo URL, sameAs URLs, contact telephone and type.

WebSite schema

Enable, plus an optional SearchAction. Google removed the sitelinks search box on 21 November 2024, so SearchAction is off by default and produces no Google feature; the option stays for other consumers.

CollectionPage schema

ItemList of the products on the current category page. Paging and sort order are honoured. Layered-navigation filters are not reflected, because the layer is not resolved yet when the page head renders.

FAQPage schema

  • Enable
  • CMS page identifiers — comma-separated allow-list. Leave empty to allow identifiers containing "faq". The homepage is never treated as an FAQ page.

Google restricts FAQ rich results to government and health sites, so treat this as machine-readable content for AI answer engines rather than a Google rich result.


FAQ page markup

Explicit attributes are the reliable option:

<div data-faq-question="What is your return policy?"
     data-faq-answer="We offer 30-day returns on all items in original condition.">
</div>

Without them the module falls back to a heuristic: <h2>/<h3> followed by <p>.


Extending with custom schemas

Implement Angeo\RichData\Api\Data\SchemaInterface and register via di.xml:

<type name="Angeo\RichData\Model\JsonLd\Renderer\SchemaRenderer">
    <arguments>
        <argument name="builders" xsi:type="array">
            <item name="my_schema" xsi:type="object">Vendor\Module\Model\JsonLd\Builder\MySchemaBuilder</item>
        </argument>
    </arguments>
</type>

Set an @id on your root node and it joins the graph like any built-in schema.


CLI validation

bin/magento angeo:rich-data:validate --store=default
bin/magento angeo:rich-data:validate --store=default --product-id=42
bin/magento angeo:rich-data:validate --category-id=11
bin/magento angeo:rich-data:validate --cms-identifier=faq
bin/magento angeo:rich-data:validate --json | jq .
Store:       default
Page URL:    https://shop.example/alpine-jacket.html
Output mode: graph

Found 3 node(s):
  Node 1: Organization
  Node 2: ProductGroup
    PASS 4 variant(s)
  Node 3: BreadcrumbList
  All nodes are merged into one @graph document.

All JSON-LD nodes look valid.

Exit code is non-zero when a node is missing an @id, an offer is incomplete or two nodes collide — usable in CI.


Tests

composer install
vendor/bin/phpunit

The Angeo AI Suite

Module Purpose
angeo/module-aeo-audit AEO audit — detects missing schema
angeo/module-rich-data This module — fixes missing schema
angeo/module-llms-txt Generates /llms.txt
angeo/module-robots-txt-aeo AI crawler rules in robots.txt
angeo/module-openai-product-feed-api ACP REST API for ChatGPT Shopping

Security

Found a vulnerability? See SECURITY.md. 2.0.0 fixes a script-breakout issue present in every 1.x release.


License

MIT — see LICENSE

Changelog

All notable changes to angeo/module-rich-data are documented here.
This project adheres to Semantic Versioning.

[2.0.0] - 2026-09-04

Security

  • JSON-LD could break out of its own <script> element. The renderer encoded
    with JSON_UNESCAPED_SLASHES and the template printed the result with
    @noEscape, so any </script> sequence in a product name, description,
    category name or configuration field ended the element early and turned the
    rest of the value into markup — a stored XSS vector reachable by anyone who can
    edit catalog text. Encoding now happens in one place
    (Model\JsonLd\JsonEncoder) with slashes escaped again and
    JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT on top, so <,
    >, &, ' and " cannot survive as raw characters. Every store running
    1.0.x–1.2.0 should upgrade.

Added

  • Linked @graph output. All schemas are now written as one
    {"@context": ..., "@graph": [...]} document, and every node carries a stable
    @id (…#product, …#organization, …#breadcrumb, …). Offers reference the
    Organization node as seller, WebSite references it as publisher, CollectionPage
    references the WebSite. In 1.x each schema was an isolated block, so nothing
    told an AI engine that the three of them described one page.
  • Output mode setting under Angeo → Rich Data → General. graph is the new
    default; legacy reproduces the 1.x one-script-per-schema output for anyone who
    needs to roll back without downgrading the package.
  • ProductGroup for configurable products. With
    Publish configurable products as ProductGroup enabled, a configurable emits
    ProductGroup with hasVariant, variesBy, productGroupID and
    inProductGroupWithID, and every variant carries its own price, stock and
    variant attributes. Supported by Google since February 2024 and used by AI
    shopping surfaces to tell one product in six sizes apart from six products.
    Capped by Maximum variants per product (default 20, hard limit 50).
  • AggregateOffer for price ranges. Configurable, grouped and bundle products
    now publish lowPrice / highPrice instead of one arbitrary number.
  • BreadcrumbList on category and CMS pages, not only product pages, with its
    own configuration group.
  • priceValidUntil window setting in days, and the option to omit the field.
  • FAQ page allow-list: CMS page identifiers under the FAQPage group.
  • CLI: --category-id, --cms-identifier and --json options on
    angeo:rich-data:validate, plus store emulation, duplicate-@id detection and
    ProductGroup checks.
  • i18n/en_US.csv, a GitHub Actions matrix on PHP 8.2–8.5, require-dev with
    PHPUnit and PHPStan, and unit tests for the encoder, renderer, product builder,
    breadcrumbs, FAQ builder and FAQ matcher.

Fixed

  • Published prices no longer ignore tax. Product::getFinalPrice() returns the
    raw catalog price with no tax adjustment, so on a store that displays prices
    including VAT the marked-up price was visibly lower than the page. Prices now
    come from the pricing framework's final_price amount, which carries the same
    tax adjustment the storefront shows.
  • Organization and WebSite no longer vanish on incomplete pages. The ViewModel
    returned an empty string as soon as current_product or current_category was
    missing, which suppressed every other schema on that page too.
  • Magento_CatalogInventory is now declared. StockRegistryInterface was used
    without the module appearing in composer.json or the sequence.
  • Out-of-stock products on MSI stores. Salability is now read through
    Product::isSalable(), which MSI plugs into; the legacy stock registry remains
    a fallback. Multi-source stores previously published sold-out items as
    InStock.
  • N+1 queries removed. Category breadcrumbs loaded one category per repository
    call — a dozen loads for a product in eight categories — and the category
    ItemList resolved fifty product URLs one query at a time. Both now use a single
    collection query.
  • Category ItemList follows the page. Paging and sort parameters are applied,
    so page 3 of a category no longer publishes the products from page 1.
  • FAQPage is no longer published on unrelated pages. 1.x emitted it on any CMS
    page containing an <h2> followed by a <p>, and on the homepage through a
    content fallback. A page must now be named in the allow-list, or have an
    identifier containing "faq". The homepage is never treated as an FAQ page.
  • FAQ heading length is measured in characters, not bytes, so short Cyrillic
    and Greek headings are filtered the same way as Latin ones.
  • priceValidUntil no longer relies on an accidental date format. 1.x used
    date('Y-12-31', …), which produced the right string only because 1, 2 and
    3 happen not to be format characters.

Changed

  • SearchAction is off by default. Google removed the sitelinks search box
    from search results on 21 November 2024, so the markup no longer produces a
    Google feature. It is harmless and the option remains for other consumers, but
    the documentation no longer advertises it as a Google feature.
  • Registry access is confined to Model\Page\CurrentEntity instead of being
    called from the ViewModel, with a repository fallback when the registry is empty.
  • Rendering logic was split out of the ViewModel into Model\Page\* services, so
    the ViewModel only assembles context.
  • Output is compact by default; set prettyPrint on JsonEncoder via di.xml to
    get readable JSON while developing.

Migration notes

See UPGRADE.md. No schema or data migration is required:

bin/magento setup:upgrade
bin/magento cache:flush

[1.2.0] - 2026-06-13

Added

  • CollectionPage + ItemList on category pages (CollectionPageBuilder).
  • Organization description.

Changed

  • ViewModel\JsonLd builds context for catalog_category_view.

[1.1.0] - 2026-06-08

Never tagged on Packagist; its changes shipped inside 1.2.0.

Added

  • Merchant return policy (offers.hasMerchantReturnPolicy).
  • Shipping details (offers.shippingDetails).
  • GTIN / MPN identifiers on Product schema. Off by default.
  • New admin config group Merchant policies (return & shipping).

Fixed

  • BreadcrumbList now actually renders on product pages. The builder expected a
    breadcrumbs context key the ViewModel never supplied.
  • availability_in_stock / availability_out_of_stock exposed in the admin.

[1.0.2] - 2026-04-18

Added

  • Initial public release: Product, Organization, WebSite, BreadcrumbList and
    FAQPage JSON-LD builders, admin configuration, angeo:rich-data:validate
    CLI command, and unit tests.
Versions
Version Stability QA Status Compatibility Released
2.0.0 stable Fail Magento 2.4.7-2.4.9 Details 2026-09-06 16:52:35
1.2.0 stable Fail Magento 2.4.7-2.4.9 Details 2026-06-14 18:54:30
1.0.2 stable Not tested Not yet tested Details 2026-04-24 20:02:53
1.0.1 stable Not tested Not yet tested Details 2026-04-18 20:45:32
1.0.0 stable Not tested Not yet tested Details 2026-04-18 19:58:31

Requires 10

Package Constraint
magento/framework >=103.0.0
magento/module-backend >=102.0.0
magento/module-catalog >=104.0.0
magento/module-catalog-inventory >=100.4.0
magento/module-cms >=104.0.0
magento/module-config >=101.2.0
magento/module-configurable-product >=100.4.0
magento/module-review >=100.4.0
magento/module-store >=101.0.0
php ~8.2.0||~8.3.0||~8.4.0||~8.5.0

Requires-dev 2

Package Constraint
phpstan/phpstan ^1.10
phpunit/phpunit ^10.5

Suggests 1

Package Reason
angeo/module-aeo-audit Verify JSON-LD injection with the AEO audit tool.

Compatibility

Each Magento release line is installed on its supported PHP versions, then the module is built (DI compilation + static-content deploy) and its unit and integration suites are run. The matrix shows the lines and PHP versions the module is confirmed to install and run on. Code-quality results further down (phpstan, phpcs, …) are reported separately and never affect compatibility.

Compatibility matrix (Magento × PHP)
Magento PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
2.4.7 Pass Pass
2.4.8 Pass Pass
2.4.9 Pass Pass

Code Quality

Advisory checks against the module's source. Static analysis runs once across the whole module; PHPStan re-runs per Magento + PHP version because resolvable symbols differ between releases. These NEVER affect the Compatibility badge. A phpcs finding can't make a module incompatible.

Static analysis

Coding standards (phpcs), mess detection (phpmd), copy-pasted code (cpd), PHP cross-version compatibility, composer.json validity. Each runs once for the whole module.

Static analysis results
Tool Status Findings Summary
PHPCS Warning 29 29 warnings (ruleset: Magento2)
PHPMD Warning 14 14 rule violations (NPathComplexity:5, CyclomaticComplexity:5, MissingImport:2, ExcessiveClassComplexity:1, UnusedFormalParameter:1)
Cpd Pass 0
Composer validate Info 10 valid; 10 advisory notes (composer validate --strict)

PHPStan

Type-checks the module's PHP against a real Magento install at the configured gate level. Re-runs per Magento and PHP version because resolvable symbols differ between releases.

PHPStan results by Magento and PHP version
Magento PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
2.4.7 6 6
2.4.8 6 6
2.4.9 6 6

Tests

Unit and integration suites, run for each applicable Magento and PHP version. A test failure speaks to the module's behaviour, not its compatibility with a Magento line, so it is reported here separately and never reddens the compatibility matrix.

Unit tests

Unit tests results by Magento and PHP version
Magento PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
2.4.7 21 21
2.4.8 21 21
2.4.9 21 21

Integration tests

Integration tests results by Magento and PHP version
Magento PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
2.4.7 N/A N/A
2.4.8 N/A N/A
2.4.9 N/A N/A

Security

Security checks run directly against the module: an audit of its declared dependencies for known vulnerabilities (composer audit) and a scan of its source for malware and web-shell signatures. Each runs once. A malware detection fails the version outright.

Security results
Tool Status Findings Summary
Composer audit Pass 0
Malware scan Pass 0
License
MIT
Homepage
https://angeo.dev
Authors

More from angeo

View vendor
angeo/module-llms-txt Free
magento2-module

Magento 2 module for AI Engine Optimization (AEO). Generates spec-compliant llms.txt and llms-full.txt per llmstxt.org standard, plus streaming JSONL for vector indexing. Multi-store, multi-website, CLI, cron, async admin UI, Page Builder-aware sanitization, customer-group pricing, atomic writes, ETag/Cache-Control, .md mirrors.

v4.3.3 4d ago
0
angeo/module-robots-txt-aeo Free
magento2-module

Magento 2 module for AI Engine Optimization (AEO). Injects AI crawler rules (OAI-SearchBot, GPTBot, ChatGPT-User, PerplexityBot, Perplexity-User, Google-Extended, ClaudeBot, anthropic-ai, Claude-User, Applebot, cohere-ai, Amazonbot, Meta-ExternalAgent) into robots.txt — without overwriting your existing configuration. Supports per-bot Allow/Disallow lists, Crawl-delay, Sitemap directives, multi-store, and a public Api\RobotsStatusInterface for cross-module integration with angeo/module-aeo-audit.

v4.0.0 14d ago
0
angeo/module-aeo-brand-visibility Free
magento2-module

Live AI brand visibility audit for Magento 2. Queries ChatGPT, Claude, Perplexity, Gemini and Groq with brand-probing prompts and scores real-world AI recall, citation rate and recommendation presence. Extends angeo/module-aeo-audit v3 via CheckerInterface as the 16th signal, alongside the 15 built-in technical checks.

v4.0.1 3d ago
0
angeo/module-aeo-audit Free
magento2-module

Magento 2 AEO (AI Engine Optimization) Audit. v3 covers 15 signals — robots.txt AI bots, llms.txt + llms.jsonl, Product / Organization / FAQ schema, merchant return + shipping policies, sitemap.xml, UCP profile, AI product feed, OG tags, canonical + hreflang, JSON-LD quality, well-known endpoint matrix, Core Web Vitals via CrUX. Score Trend dashboard, Admin UI, cron, dynamic fix commands, dependency-injected extension point for custom checkers.

v4.2.3 4d ago
0
Make it pay

Turn an existing module into recurring revenue.

If you already maintain a Magento 2 module on GitHub or GitLab, listing it on Packagento takes about five minutes. We mirror your tags, handle distribution signing, and route paid licenses through Stripe Connect, so you can keep shipping the way you already do.