Browse Source

SEO: add BlogPosting + BreadcrumbList JSON-LD

Only Person + WebSite identity JSON-LD was emitted site-wide; posts/pages had no
per-content schema (the one BlogPosting was microdata inside the now-removed dead
scroll template). Add to inc/seo.php on wp_head (so it covers the block templates):
- Person gets a stable @id (#person).
- BlogPosting on single posts (headline, datePublished/dateModified, author +
  publisher -> the Person @id, image, description).
- BreadcrumbList for hierarchical pages, posts (Home > category > post), and term
  archives.
windhamdavid 2 weeks ago
parent
commit
788307c941
1 changed files with 79 additions and 0 deletions
  1. 79 0
      inc/seo.php

+ 79 - 0
inc/seo.php

@@ -132,6 +132,7 @@ function dw_seo_jsonld() {
 	$person = array(
 	$person = array(
 		'@context' => 'https://schema.org',
 		'@context' => 'https://schema.org',
 		'@type'    => 'Person',
 		'@type'    => 'Person',
+		'@id'      => DW_CANONICAL . '/#person', // stable identity per-page nodes reference
 		'name'     => DW_SITE_NAME,
 		'name'     => DW_SITE_NAME,
 		'url'      => DW_CANONICAL,
 		'url'      => DW_CANONICAL,
 		'image'    => DW_CANONICAL . '/og-image.jpg',
 		'image'    => DW_CANONICAL . '/og-image.jpg',
@@ -162,10 +163,88 @@ function dw_seo_jsonld() {
 	echo '<script type="application/ld+json">' . wp_json_encode( $website, JSON_UNESCAPED_SLASHES ) . "</script>\n";
 	echo '<script type="application/ld+json">' . wp_json_encode( $website, JSON_UNESCAPED_SLASHES ) . "</script>\n";
 }
 }
 
 
+/** BlogPosting JSON-LD on single posts. author/publisher reference the site Person
+ *  (@id) emitted by dw_seo_jsonld() on the same page, so it all resolves to one entity. */
+function dw_seo_jsonld_article() {
+	if ( ! is_single() ) {
+		return;
+	}
+	$id     = get_queried_object_id();
+	$url    = get_permalink( $id );
+	$author = array( '@type' => 'Person', '@id' => DW_CANONICAL . '/#person', 'name' => DW_SITE_NAME );
+	$node   = array(
+		'@context'         => 'https://schema.org',
+		'@type'            => 'BlogPosting',
+		'@id'              => $url . '#article',
+		'mainEntityOfPage' => $url,
+		'url'              => $url,
+		'headline'         => get_the_title( $id ),
+		'datePublished'    => get_the_date( 'c', $id ),
+		'dateModified'     => get_the_modified_date( 'c', $id ),
+		'author'           => $author,
+		'publisher'        => $author,
+		'image'            => dw_seo_image(),
+		'description'      => dw_seo_description(),
+	);
+	echo '<script type="application/ld+json">' . wp_json_encode( $node, JSON_UNESCAPED_SLASHES ) . "</script>\n";
+}
+
+/** BreadcrumbList JSON-LD — hierarchical pages (Home > ancestors > page), posts
+ *  (Home > first category > post), and term archives (Home > term). */
+function dw_seo_jsonld_breadcrumbs() {
+	if ( is_front_page() ) {
+		return;
+	}
+	$crumbs = array( array( 'name' => 'Home', 'url' => home_url( '/' ) ) );
+
+	if ( is_singular() ) {
+		$id = get_queried_object_id();
+		if ( is_page() ) {
+			foreach ( array_reverse( get_post_ancestors( $id ) ) as $ancestor ) {
+				$crumbs[] = array( 'name' => get_the_title( $ancestor ), 'url' => get_permalink( $ancestor ) );
+			}
+		} elseif ( is_single() ) {
+			$cats = get_the_category( $id );
+			if ( ! empty( $cats ) ) {
+				$crumbs[] = array( 'name' => $cats[0]->name, 'url' => get_category_link( $cats[0]->term_id ) );
+			}
+		}
+		$crumbs[] = array( 'name' => get_the_title( $id ), 'url' => get_permalink( $id ) );
+	} elseif ( is_category() || is_tag() || is_tax() ) {
+		$term = get_queried_object();
+		$link = get_term_link( $term );
+		$crumbs[] = array( 'name' => $term->name, 'url' => is_wp_error( $link ) ? home_url( '/' ) : $link );
+	} else {
+		return; // home / search / date views get no meaningful trail
+	}
+
+	if ( count( $crumbs ) < 2 ) {
+		return;
+	}
+
+	$items = array();
+	foreach ( $crumbs as $i => $c ) {
+		$items[] = array(
+			'@type'    => 'ListItem',
+			'position' => $i + 1,
+			'name'     => $c['name'],
+			'item'     => $c['url'],
+		);
+	}
+	$node = array(
+		'@context'        => 'https://schema.org',
+		'@type'           => 'BreadcrumbList',
+		'itemListElement' => $items,
+	);
+	echo '<script type="application/ld+json">' . wp_json_encode( $node, JSON_UNESCAPED_SLASHES ) . "</script>\n";
+}
+
 /** Emit the whole head block. Hooked to wp_head; front-page.php calls it directly. */
 /** Emit the whole head block. Hooked to wp_head; front-page.php calls it directly. */
 function dw_seo_head() {
 function dw_seo_head() {
 	dw_seo_favicon();
 	dw_seo_favicon();
 	dw_seo_opengraph();
 	dw_seo_opengraph();
 	dw_seo_jsonld();
 	dw_seo_jsonld();
+	dw_seo_jsonld_article();
+	dw_seo_jsonld_breadcrumbs();
 }
 }
 add_action( 'wp_head', 'dw_seo_head', 5 );
 add_action( 'wp_head', 'dw_seo_head', 5 );