Skip to content

TigerSEO: JSON-LD site-identity graph (Organization + WebSite + SiteNavigation)#13

Merged
WebTigers merged 1 commit into
mainfrom
feat/seo-jsonld
Jul 17, 2026
Merged

TigerSEO: JSON-LD site-identity graph (Organization + WebSite + SiteNavigation)#13
WebTigers merged 1 commit into
mainfrom
feat/seo-jsonld

Conversation

@WebTigers

Copy link
Copy Markdown
Owner

The rich-results / sitelinks layer — the JSON-LD that Phase 3's sitelinks discussion was aiming at. Where Seo_Service_Head describes one page (title/OG/canonical), the new Seo_Service_Schema describes the site as an entity.

One @graph, three nodes

A single <script type="application/ld+json"> block, cross-referenced by @id:

  • Organization — the brand: name, logo (resolved through the media row → a real absolute URL + true width/height), and social sameAs links. This is the entity a knowledge panel / brand sitelinks attach to.
  • WebSitepublisher → the Organization; an optional SearchAction (the Google sitelinks searchbox) emitted only when tiger.seo.schema.search_url is configured.
  • SiteNavigationElement — the primary menu (Tiger_Menu::getData), top-level items, so Google can map site structure into nav sitelinks.

Same seam as the OG tags

Seo_Plugin_Head calls Seo_Service_Schema::emitSite() once per request (the service latches — the site graph is identical on every page). It appends into a process-wide tigerJsonLd Zend_View placeholder that the public layout renders — no core edit, and it's gone when the module is uninstalled (non-public layouts just don't output the placeholder). Not a Tiger_Service_Service, so unreachable over /api.

Config-driven + safe

Everything reads the config cascade (tiger.site.name/.logo, tiger.seo.social.*, tiger.seo.schema.*) — org-scoped, live-override. Fail-soft: a missing name/logo/menu just omits that node; a broken lookup never breaks the page. </> are hex-escaped (JSON_HEX_TAG) so no field value can break out of the <script> (verified: a <script> in a value serializes to <script>, still valid JSON).

Verified

Smoke now asserts the seeded page carries an application/ld+json block with the Organization node and that the block parses as valid JSON.

Next slice: BreadcrumbList (per-page trail) + Article (blog rich result), which build on the same graph.

🤖 Generated with Claude Code

…avigation)

The rich-results / sitelinks layer. Where Seo_Service_Head describes one page, the new
Seo_Service_Schema describes the SITE as an entity — a single ld+json @graph:

- Organization — brand name, logo (media row → real absolute URL + dimensions), sameAs
  social links. Config: tiger.site.name / tiger.site.logo / tiger.seo.social.*.
- WebSite — publisher → Organization; optional SearchAction (sitelinks searchbox) when
  tiger.seo.schema.search_url is configured.
- SiteNavigationElement — the primary menu (Tiger_Menu::getData), so Google can map site
  structure into nav sitelinks.

Emitted once per request (the service latches) by Seo_Plugin_Head into a process-wide
'tigerJsonLd' Zend_View placeholder that the public layout renders — same contributed-head
seam as the OG tags, no core edit, degrades to nothing without the module. Fail-soft
throughout; < and > are hex-escaped via JSON_HEX_TAG so no value can break out of the
<script>. Smoke asserts the seeded page carries a valid Organization ld+json block.

BreadcrumbList + Article are the next slice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@WebTigers
WebTigers merged commit 588eb41 into main Jul 17, 2026
5 checks passed
@WebTigers
WebTigers deleted the feat/seo-jsonld branch July 17, 2026 16:05
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.

1 participant