• Home
  • Blog
  • Practical AMP: adjust structured data for Recipes sites

Practical AMP: adjust structured data for Recipes sites

Example of filtering AMP doc type

 

We just had a customer asking about how to efficiently improve the structured data on their AMP pages. Being such a major SEO optimization one can make, especially for a Recipes site, I thought this would be a good example of using one of many weeblrAMP filters and actions. And so here is the first installment of our #practicalAMP series!

The site in question has both a number of informational pages and then a very large number of recipes. When creating AMP versions of all those pages, weeblrAMP automatically adds AMP-required minimal schema.org structured data, and then some depending on the document type. Which is why it is important to recognize the document type, when possible.

weeblrAMP lets you select the default document type, and then add some shortcode in your content if you need to specify one for some post or page. Not viable for several hundreds existing recipes though, and that's where WordPress filters get into action...

The Recipes on that site were of 2 kinds:

  • Regular posts
  • Custom post types as managed by a recipe plugin

 Changing the posts doc type

We used the weeblramp_json_manifest filter, which is documented here. It receives an array of structured data gathered so far, and lets us modify it on the fly.

But in this case, as only some posts are recipes and other are just informational pages, we certainly did not want to set the product type to Recipes for all. But our filter also provides your with all available data about the page! We can thus check the post categories, and only change the structured data for the right posts. 

The code

 


/**
 * 1. Change the document type based on the request.
 * This filter is triggered BEFORE reading user-defined data from the content
 * (ie: wbamp-meta shortcodes)
 */
add_filter(
	'weeblramp_json_manifest',
	function ( $jsonld, $pageData ) {

		// set document type based on post
		$post = WeeblrampHelper_Content::getPostFromPageData( $pageData );
		if ( ! empty( $post ) && 'single' == wbArrayGet( $pageData, 'request_type' ) ) {
			$postCategories   = wp_get_post_categories( $post->ID, array('fields' => 'ids' );
			$recipeCategories = array( 12, 34, 56);
			// set document type
			if ( array_intersect( $postCategories, $recipeCategories ) ) {
				$jsonld['@type'] = 'Recipe';
			}
		}

		return $jsonld;
	},
	10,
	2
);

 

What does it do?

 First we use the WordPress add_filter function to register for the weeblramp_json_manifest filter. Later, before it inserts the stuctured data into an AMP page, weeblrAMP will call our filter function.

When that happens:

  • We extract the post information from the page data, using a helper function WeeblrampHelper_Content::getPostFromPageData.
  • If we do have a post, and the request is for a "single" page, then we use wp_get_post_categories to obtain the post category(ies).
  • We check if the post is in one of the Recipes categories. We simply hardcoded the Recipes categories ids in that example.
  • If so, we set the @type value of the Json-ld array, to replace the previous value.

That's it! weeblrAMP will now use the Recipe structured data set. Knowing this page is a recipe, weeblrAMP will also automatically add a name field to the structured data, because this is a requirement for Recipes (at least for Google!).

Possible improvements

We could also set other important structured data element in the same go, such as prepTime, cookTime or recipeIngredient for instance. The exact code to use depends on how this information is stored.

This can be done with another weeblrAMP filter, in very much the same way:


/**
 * 2. Filter json-ld data to add support for recipes.
 * This filter is triggered AFTER reading user-defined data from the content
 * (ie: wbamp-meta shortcodes)
 */
add_filter(
	'weeblramp_get_jsonld_data',
	function ( $jsonld, $pageData ) {

		$docType = wbArrayGet( $jsonld, '@type' );
		if ( 'Recipe' == $docType ) {
			// this is a Recipe, let's add cooking time and such
			$jsonld['cookTime']         = 'PT1H';
			$jsonld['prepTime']         = 'PT30M';
			$jsonld['recipeIngredient'] = array(
				'Cheese',
				'Sugar',
				'Lemon'
			);
		}

		return $jsonld;
	},
	10,
	2
);

Additionally, for pages built using their Recipe management plugin, this plugin is already building a full set of structured data, and we do expect to be able to collect them, and re-use them on AMP page, just like we changed the document type.

And what do we get with all this?

Well, the default schema.org structured data is adjusted now for recipes posts to something like this:


{
	"@context": "http://schema.org",
	"@type": "Recipe",
	"mainEntityOfPage": "https://www.example.com/2017/05/01/recipe-post/amp/",
	"headline": "Recipe post – Example.com",
	"image": {
		"@type": "ImageObject",
		"url": "https://www.example.com/wp-content/uploads/2016/10/839554fd-2361-3d4e-852c-1f675e1f4481.jpg",
		"width": 1437,
		"height": 958
	},
	"publisher": {
		"@type": "Organization",
		"name": "WeeblrPress",
		"url": "https://www.example.com",
		"logo": {
			"@type": "ImageObject",
			"url": "https://www.example.com/wp-content/uploads/2016/09/weeblrpress-logo-text-w250-h60-with-border.png",
			"width": 250,
			"height": 60
		}
	},
	"datePublished": "2017-05-01T12:26:51Z",
	"dateModified": "2017-05-01T16:17:47Z",
	"author": {
		"@type": "Person",
		"name": "admin"
	},
	"name": "Recipe post",
	"cookTime": "PT1H",
	"prepTime": "PT30M",
	"recipeIngredient": ["Cheese", "Sugar", "Lemon"]
}

We have used a pretty display here for convenience, but when inserted into an AMP page, this JSON will be minified, to save space.

But wait, where should I put this code?

Good question! Normally, you would put any such filter in your theme functions.php. Problem is on AMP pages, your theme is not used at all, not even loaded.

That's why weeblrAMP provides an "alternate" functions.php, loaded only on AMP pages. You only need to do one thing: put your functions.php file in a weeblramp subfolder of your theme. Of course, we strongly advise to use a child-theme for that, which will prevent your custom changes to be overriden during next update of your theme! Here is the result:

 

Example of filtering AMP doc type

Hope you find these snippets useful, certainly more to come!

 Yannick