Add GTIN to WooCommerce Product Schema

EAN13 Product id tutorial for WooCommerce

In this WordPress & WooCommerce tutorial, we’re going to fix the “No global identifier provided (e.g. GTIN)” warning in Google Search Console. To do this, we need to be able to store the GTIN, GTIN12, GTIN13 (or whatever) against the WooCommerce product. Then we need to hook into WooCommerce when it creates the structured data (schema). We’re only going to cover Simple product types here. If there is demand, we’ll consider revising the tutorial to cover variable products too.

infoThis is very similar to our tutorial that adds Brand Name to a product’s schema. In fact, this tutorial works quite happily alongside it.

We’ll need to do three things here:

  1. Add some text input boxes in the back-end so we can edit GTIN, GTIN13, MPN, EAN13, etc. at product-level.
  2. Write some back-end code that captures these fields when the product is saved, then store these values in the product’s post_meta.
  3. Hook WooCommerce when it outputs the product schema and inject our fields into the structured data.
Add GTIN to WooCommerce Product Schema video tutorial
Video walk-through for this tutorial

Scaffold the Code

To get things started, go in to your WordPress child theme, create a text file called “wpt-product-identifiers.php” and paste the following into it.

<?php

/**
 * WP Tutorials : Product identifiers GTIN13 EAN, ISBN (WPTPIDS)
 *
 * https://wp-tutorials.tech/refine-wordpress/add-gtin-to-product-schema/
 */

defined('WPINC') || die();

const WPTPID_META_PRODUCT_GTIN = 'product_gtin';
const WPTPID_META_PRODUCT_GTIN13 = 'product_gtin13';
const WPTPID_META_PRODUCT_MPN = 'product_mpn';

function wptpid_get_product_id_types() {
	global $wptpid_product_id_types;

	if (is_null($wptpid_product_id_types)) {
		$wptpid_product_id_types = array(
			WPTPID_META_PRODUCT_GTIN => array(
				'label' => 'GTIN',
				'description' => 'Global Trade Item Number',
				'schema_key' => 'gtin',
			),
			WPTPID_META_PRODUCT_GTIN13 => array(
				'label' => 'GTIN 13',
				'description' => 'Global Trade Item Number (13)',
				'schema_key' => 'gtin13',
			),
			WPTPID_META_PRODUCT_MPN => array(
				'label' => 'MPN',
				'description' => 'Manufacturer Part Number',
				'schema_key' => 'mpn',
			),
			// Add your own idintifiers in here...
			// ...
		);
	}

	return $wptpid_product_id_types;
}

/**
 * When editing a WooCommerce product, render HTML inputs fields for our
 * product identifiers.
 */
function wptpid_render_simple_product_unique_ids() {
	// Render the <input type="text" /> controls on the product page.
}
add_action('woocommerce_product_options_general_product_data', 'wptpid_render_simple_product_unique_ids');

/**
 * Hook into WooCommerce save product action, look for GTIN/etc in the $_POST
 * data and save it in the product's post meta.
 */
function wptpid_save_simple_product_unique_ids($product_id) {
	// Analyse the $_POST data and save GTIN, MPN, etc in the product's post meta.
}
add_action('woocommerce_process_product_meta', 'wptpid_save_simple_product_unique_ids', 10, 1);

/**
 * Add GTIN/etc to the product's schema (structured data).
 */
function wptid_add_unique_ids_to_product_schema($data) {
	// Adjust the product schema.

	return $data;
}
add_filter('woocommerce_structured_data_product', 'wptid_add_unique_ids_to_product_schema');

We’ve created a function called wptpid_get_product_id_types() that returns an array with all our info in it. This is so we can add new global product identifiers if we want to. For now, we’ll keep things simple.

Next, open your child theme’s functions.php and add the following couple of lines:

// WP Tutorials Product identifiers GTIN13 MPN, etc.
require_once dirname(__FILE__) . '/wpt-product-identifiers.php';

Edit and Save Product Settings

Let’s get product-editing sorted out first. This is what we’re aiming for when editing a simple product:

Edit the GTIN on a WooCommerce product
Edit the GTIN for a WooCommerce product

To render the input boxes, we need to grab the result from wptpid_get_product_id_types(), loop through the fields and call woocommerce_wp_text_input() to render each one. Paste the following into the wptpid_render_simple_product_unique_ids() function:

function wptpid_render_simple_product_unique_ids() {
	$unique_id_types = wptpid_get_product_id_types();

	if (empty($product = wc_get_product())) {
		// ...
	} elseif ($product->is_type('variable')) {
		// Variable products work differently.
	} else {
		echo '<div class="options_group">';
		foreach ($unique_id_types as $field_name => $meta) {
			woocommerce_wp_text_input(
				array(
					'id' => $field_name,
					'label' => $meta['label'],
					'description' => $meta['description'],
					'desc_tip' => true,
					'default' => '',
				)
			);
		}
		echo '</div>'; // .options_group
	}
}

There’s nothing magic in here. All we do is verify that we can get a handle to the current WooCommerce product, and it’s not a variable product. Then we just loop through our custom fields.

Next, paste the following into wptpid_save_simple_product_unique_ids() to capture and save the product’s meta data when we save a product:

function wptpid_save_simple_product_unique_ids($product_id) {
	$unique_id_types = wptpid_get_product_id_types();

	foreach ($unique_id_types as $field_name => $meta) {
		$field_value = array_key_exists($field_name, $_POST) ? sanitize_text_field($_POST[$field_name]) : '';

		if (!empty($field_value)) {
			update_post_meta($product_id, $field_name, $field_value);
		} else {
			delete_post_meta($product_id, $field_name);
		}
	}
}

Save all your changes to “wpt-product-identifiers.php” and edit a product to check that the fields are displayed.

Hook the Product Schema

According to the Product schema specification, we need to add the new fields to the structured data like this:

{
	"@context": "https://schema.org/",
	"@type": "Product",
	...
	...
	...
	"gtin": "AAAABBBBCCCC",
	"gtin13": "aaaabbbbcccc",
	"mpn": "000011112222"
}

So all we need to do is loop through the fields from wptpid_get_product_id_types() again and use get_post_meta( $product_id, $field_name, true ) to grab the GTIN/MPN and add it to the schema array.

Paste the following into the wptid_add_unique_ids_to_product_schema() function:

function wptid_add_unique_ids_to_product_schema($data) {
	if (!is_array($data)) {
		// If $data isn't an array, don't do anything.
	} elseif (empty($product = wc_get_product())) {
		// Don't do anything.
	} elseif ($product->is_type('variable')) {
		// Variable products work differently.
	} else {
		$product_id = $product->get_id();
		$unique_id_types = wptpid_get_product_id_types();
		foreach ($unique_id_types as $field_name => $meta) {
			$field_value = strval(get_post_meta($product_id, $field_name, true));
			$schema_key = $meta['schema_key'];

			if (!empty($field_value)) {
				$data[$schema_key] = $field_value;
			}
		}
	}

	return $data;
}

Deploy and Test

That’s about it – now we just need to test it.

tipIf you’re using a page caching plugin on your site, flush your page cache before testing changes.

Go to the Schema Structured Data Testing Tool and run a test against your product’s URL. When you expand the Product section, you should see something like this (with more sensible data, of course):

GTIN, GTIN13 and MPN in a WooCommerce product schema
GTIN and MPN in a WooCommerce product schema

When you’re happy with how your product schema looks, prompt Google Search Console to re-index your page(s) and your warning should be fixed in a few days πŸ˜ŽπŸ‘

Like This Tutorial?

Let us know

WordPress plugins for developers

11 thoughts on “Add GTIN to WooCommerce Product Schema”

  1. Your stuff is trully useful and incredible. Still waiting for tooltip glossary like popup on words!

    Popper.js (boostrap tooltip style) or floating-ui would be awesome!

    Many thanks☺️

    Reply
    • Cheers!

      Popper.js looks quite fun.

      I guess… using a Custom Post Type to build the glossary, then doing some analysis in JavaScript to split the rendered paragraphs into words and compare them against an array of glossary-terms… yes. I think it could be a neat tutorial without too much work. I wonder if we could add some SEO value too? I’ll have a think.

      Watch this space…

      Reply
      • Oh yeah.. 😎
        Fyi: Popper is included into boostrap, and btw its powering 80% of Microsoft teams. Just incredible how a js library can hold an entire software.. Waiting for your goldweight tutorial!

        Reply
    • You will be fine as long as you’ve put the code into a child theme or you’re using a PHP snippets plugin.

      I’ve been running this code on multiple sites for a while (in child themes) and there have been no problems with WooCommerce updates.

      Reply
  2. At first I was getting along but when it get to some point I got lost. I wish there was a video explanation of this from you.

    Nevertheless, thanks

    Reply
    • Hi. I’ve made a video walk-through and added it to the top of the post (hosted on YouTube). It’s the first walk-through I’ve recorded for the site, so the quality is not great. Hopefully it will help you out though πŸ‘

      Reply
    • It’s a tricky one for variable products. If you paste the URL for one of your variable products into the Structured Data Testing Tool, you’ll see that your product doesn’t actually have an “Offer” element. Instead it’s got an “AggregateOffer”, which has a price range covering the cheapest to the most expensive variation. So it’s quite a bit of work to make this function for variable products because we need to discard the WooCommerce Offer element and replace it with multiple Offer elements, each with their own GTIN/EAN. It’s beyond the scope of this tutorial.

      What most of my WooCommerce clients do is rely on a Google Merchant Center feed to supply GTIN/EAN for individual variations.

      Reply

Leave a comment