SEO: convert schema output to an @graph and add a site-level Organization node#50080
SEO: convert schema output to an @graph and add a site-level Organization node#50080gmjuhasz wants to merge 4 commits into
Conversation
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖 Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! |
Code Coverage SummaryCoverage changed in 1 file.
4 files are newly checked for coverage.
|
There was a problem hiding this comment.
Pull request overview
Refactors the SEO schema emitter to output a Schema.org JSON-LD @graph document on singular requests, introducing a site-level Organization node and wiring it as the Article publisher via stable @id values. This establishes the shared graph foundation for future site-level schema nodes.
Changes:
- Convert
Schema_Builderfrom single-node emission to assembling/serializing a multi-node@graphdocument. - Add schema infrastructure services:
Schema_Graph(collector/serializer) andSchema_Node_Ids(stable site-anchored@ids). - Extract per-post schema building into
Post_Schema_Nodeand addOrganization_Schema_Nodewith settings seams + new PHPUnit coverage.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| projects/packages/seo/src/class-schema-builder.php | Switches emission to @graph document assembly and serialization; wires Article → Organization publisher reference. |
| projects/packages/seo/src/class-schema-graph.php | Adds a small graph collector that skips empty nodes and renders { @context, @graph } or null. |
| projects/packages/seo/src/class-schema-node-ids.php | Provides stable site-root anchored node @id helpers (e.g., #organization). |
| projects/packages/seo/src/class-post-schema-node.php | Moves Article/FAQPage node building into a dedicated builder used by the graph. |
| projects/packages/seo/src/class-organization-schema-node.php | Introduces Organization node builder from site identity, with optional settings overrides/sanitization. |
| projects/packages/seo/tests/php/SchemaBuilderTest.php | Updates tests to assert the actual emitted <script type="application/ld+json"> document shape and wiring. |
| projects/packages/seo/tests/php/SchemaGraphTest.php | Adds unit tests for graph behavior (empty graph, skipping null/empty nodes, preserving order). |
| projects/packages/seo/tests/php/SchemaNodeIdsTest.php | Adds unit tests for stable Organization @id generation. |
| projects/packages/seo/tests/php/PostSchemaNodeTest.php | Adds unit tests for the extracted per-post node builder (Article + FAQPage). |
| projects/packages/seo/tests/php/OrganizationSchemaNodeTest.php | Adds unit tests for Organization node outputs, sanitization, and logo fallbacks. |
| projects/packages/seo/changelog/add-seo-schema-organization-graph | Adds a changelog entry describing the new Organization node and @graph output. |
Part of JETPACK-1779
Back-end foundation slice for the Site Schemas milestone: converts the SEO schema output to a multi-node
@graphand adds the first site-level node (Organization). This is the shared@graphfoundation the other site-level schema types (WebSite, LocalBusiness, Person) build on.Scope — this PR is the
@graphfoundation + Organization node only. It does not close JETPACK-1779's settings acceptance:@graph(Article/FAQ preserved); emit a site-level Organization node from existing site identity; reference it from the Articlepublisher.sameAs, field overrides) → settings-server PR; the "Organization / Business info" settings UI → settings-UI PR. The Organization builder already accepts a$settingsarray; the graph passes it empty for now, so social profiles / overrides are intentionally not configurable yet.Proposed changes
Schema_Builder::emit()from a single-node emitter into a thin@graphserializer.build_document()assembles the graph and wires cross-node references (Article → Organizationpublisherby@id); the individual node builders stay self-contained and unaware of each other.projects/packages/seo/src/:Schema_Graph— collects nodes and renders the{ @context, @graph }document (or null when empty).Schema_Node_Ids— stable, site-root-anchored@ids (e.g.https://example.com/#organization) so nodes cross-reference reliably across every page.Post_Schema_Node— the Article / FAQPage builder, moved out ofSchema_Builderunchanged.Organization_Schema_Node— site-level Organization node built from existing site identity (Site Title, home URL, Site Logo → Site Icon fallback, Tagline).sameAs/email/ overrides come from an optional$settingsseam.is_singular(). Site-level nodes ride along on the singular request's graph, so the home page, archives, 404s, drafts, and override-less pages still emit nothing.SchemaBuilderTestnow asserts the real emitted<script type="application/ld+json">document (the actual regression surface); newPostSchemaNodeTest,SchemaGraphTest,OrganizationSchemaNodeTest. Adds the emitted-output regression harness for the current Article/FAQ behavior before the conversion.Example emitted output for a published post (site with a Site Logo and Tagline):
{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://example.com/#organization", "name": "Acme Co", "url": "https://example.com/", "description": "We make fine widgets", "logo": { "@type": "ImageObject", "url": "https://example.com/logo.png", "width": 120, "height": 60 } }, { "@type": "Article", "headline": "Hello world", "datePublished": "2026-01-01T00:00:00+00:00", "dateModified": "2026-01-02T00:00:00+00:00", "mainEntityOfPage": { "@type": "WebPage", "@id": "https://example.com/hello-world/" }, "author": { "@type": "Person", "name": "Jane Doe" }, "publisher": { "@id": "https://example.com/#organization" } } ] }Feature flag
Inherits the existing SEO gate —
Schema_Builderonly loads when thersm_jetpack_seofeature flag is on (default off) and the SEO Tools module is active. Sites without the flag see no change at all; only the flagged new-SEO cohort gets the new@graph+ Organization output. No new flag added.Related product discussion/links
Does this pull request change what data or activity we track or use?
No.
Testing instructions
Setup
add_filter( 'rsm_jetpack_seo', '__return_true' );(snippet / mu-plugin) and make sure the SEO Tools module is active.New behavior —
@graph+ Organization<script type="application/ld+json">block and confirm:@grapharray (not a single top-level node).name(Site Title),url,logo(your Site Logo/Icon as anImageObject), anddescription(Tagline).publisherreferencing the Organization's@id(e.g.https://your-site/#organization).No regression
headline,datePublished,dateModified,author,mainEntityOfPage, andimagewhen a featured image is set).publisher).Automated
pnpm jetpack test php packages/seo(orcomposer phpunitinprojects/packages/seo) → 46 pass.pnpm jetpack phan packages/seo→ 0 issues.