GTIN numbers for variable products

#69577
  • Resolved Jason Jeffers
    Rank Math free

    We have a series of products each with variations in liters. For merchant centers, apparently we need to output individual offers within the Offers -> AggregateOffer attributes with @type: offer, price, priceCurrency, sku, name, availability and most importantly gtin13. I’ve seen variations of code to achieve parts of this equation but am struggling to output the proper format in a foreach loop. The only way I have found is to use the [$key] index entity. Unfortunately, this adds an index to the Offers -> AggregateOffers which of course is not a valid attribute:

    “offers”: {
    “0”: { // The property 0 is not recognized by Google for an object of type AggregateOffer.
    “offer”: {
    “@type”: “Offer”,
    “name”: “1 Liter”,
    “sku”: “1-liter”,
    “gtin13”: “7424903198115”,
    “price”: “19.29”,
    “priceCurrency”: “EUR”,
    “availability”: “InStock”
    }
    }, etc.

    Without the [$key] I can only output the last variation. The rest of the code gathers the necessary fields properly. Here is the complete code I am using:

    add_filter( 'rank_math/snippet/rich_snippet_product_entity', function( $entity ) {
        global $product;
        $entity['gtin8'] = $product->get_sku();
        if ( $product->is_type( 'variable' ) ) {
            $variations = $product->get_available_variations();
            foreach ( $variations as $key => $variation ) {
                foreach ($variation['attributes'] as $attribute => $term_slug ) {
                    $taxonmomy = str_replace( 'attribute_', '', $attribute );
                    $name   = get_term_by( 'slug', $term_slug, $taxonmomy )->name;
                    $sku    = get_term_by( 'slug', $term_slug, $taxonmomy )->slug;
                    $gtin   = get_post_meta( $variation['variation_id'], 'custom_gtin_field', true );
                    $price  = $variation['display_regular_price'];
                    $price  = number_format((float)$price, 2, '.', '');
    
                    $entity['offers'][ $key ][ 'offer' ]['@type']           = 'Offer';
                    $entity['offers'][ $key ][ 'offer' ]['name']            = $name;
                    $entity['offers'][ $key ][ 'offer' ]['sku']             = $sku;
                    $entity['offers'][ $key ][ 'offer' ]['gtin13']          = $gtin;
                    $entity['offers'][ $key ][ 'offer' ]['price']           = $price;
                    $entity['offers'][ $key ][ 'offer' ]['priceCurrency']   = "EUR";
                    $entity['offers'][ $key ][ 'offer' ]['availability']    = "InStock";
                }
            }
        } else {
            $entity['gtin8'] = $product->get_sku();
        }
        return $entity;
    });

    As reference, here is one of the products on our site: https://www.ecoformeurope.com/product/cementmix-diy-make-waterproof-cement/

    Hopefully you can help getting this to output properly.

    Cheers,

    Jason Jeffers

Viewing 4 replies - 1 through 4 (of 4 total)
  • Hello,

    Assuming you are using Rank math with WooCommerce, please note that Rank Math takes the schema data from your product pages automatically and turns it into Schema-ready content. Likewise, the aggregateRating and review fields are taken from your actual product reviews that your buyers/customers leave on your products.

    For the GTIN, ISBN, MPN issue, you can fix it by adding that to your products using the custom product attribute feature in WooCommere or you can use this plugin: https://wordpress.org/plugins/product-gtin-ean-upc-isbn-for-woocommerce/ . You can add the following filter to add GTIN for variable products.

    
    /**
    * Filter to add GTIN to Variable Products.
    *
    * @param array $entity Snippet Data
    * @return array
    */
    add_filter( 'rank_math/snippet/rich_snippet_product_entity', function( $entity ) {
       if(is_product()){
         $product = new WC_Product( get_the_ID() );
         if( $product->is_type( 'simple' ) ){
           $gtin_value = get_post_meta($product->get_id(),'_wpm_gtin_code', true);
           $entity['gtin13'] = $gtin_value;
         }elseif($product->is_type( 'variable' )){
           $variation = new WC_Product_Variation(get_the_ID());
           $gtin_value = get_post_meta($variation->get_id(),'_wpm_gtin_code', true);
           $entity['gtin13'] = $gtin_value;
         }
       }
       
       return $entity;
    });
    

    Hope that helps and please do not hesitate to let us know if you need our assistance with anything else.

    Michael,

    I don’t think I explained the problem properly as the answer was a copy/paste from other answers I have seen throughout the support forums. In any case, I will try to make it clear what our problem is and how your solution does not address the problem:

    We have 6 products, each with 5 or 6 variations (different amounts of liters). In order to sell these products on merchant centers such as Google or Amazon we need to output each variation as a separate offer within the schema data and each of these offers must contain its own GTIN (already created and accessible as a custom field contained in the post_meta), price, priceCurrency, etc. As you know, the standard output of Woocommerce for a product with variations is to include them as an “grouped” offerCount to an AggregateOffer attribute like the following:

    "offers": {
            "@type": "AggregateOffer",
            "lowPrice": "19.29",
            "highPrice": "226.80",
            "offerCount": 5,
            "priceCurrency": "EUR",
            "availability": "http://schema.org/InStock",
            "seller": {
              "@type": "Organization",
              "@id": "https://www.ecoformeurope.com/",
              "name": "Ecoform Europe",
              "url": "https://www.ecoformeurope.com",
              "logo": "logo"
            },
            "url": "product URL"
          }

    However, as I said, we need to output each variation as a single offer within an attribute “Offers”, i.e. separate offers. AggregateOffers alone doesn’t cut it for our situation. In fact, I’m pretty sure, correct me if I’m wrong, that in our case we don’t even need AggregateOffers as we want to represent each variation separately for the merchant centers, i.e. sell each different liter container as a separate product. We use, as most people do, Woocommerce variations as opposed to single products for clarity and maintenance; one product, multiple variations. So what we are looking for is this:

    "offers": [{
        "@type": "Offer",
        "sku": "egsoapsuds-250",
        "name": "Soap Suds 250 ml",
        "price": 5.99,
        "priceCurrency": "EUR",
        "availability": "InStock"
      },{
        "@type": "Offer",
        "sku": "egsoapsuds-500",
        "name": "Soap Suds 500 ml",
        "price": 10.99,
        "priceCurrency": "EUR",
        "availability": "OutOfStock"
      },{
        "@type": "Offer",
        "sku": "egsoapsuds-1000",
        "name": "Soap Suds 1000 ml",
        "price": 17.99,
        "priceCurrency": "EUR",
        "availability": "InStock"
      }]

    As I mentioned earlier, and represented in my own code in the first message, I was able to come close to the desired output. However, the only way I could achieve outputting all the variations instead of just the first variation, was to use the [$key] entity as the multiple foreach loops did not work without it. Unfortunately, this resulted in the following output:

    “offers”: {
    “0”: { // The property 0 is not recognized by Google for an object of type AggregateOffer.
    “offer”: {
    “@type”: “Offer”,
    “name”: “1 Liter”,
    “sku”: “1-liter”,
    “gtin13”: “7424903198115”,
    “price”: “19.29”,
    “priceCurrency”: “EUR”,
    “availability”: “InStock”
    }
    }, etc.

    Notice the “0” attribute created by the [$key] entity. So basically, if you look at my code, how can I output all the variations as separate offers using the foreach loop without using the [$key] entity? It seems that somehow the foreach loops are not outputting correctly even though this code is based on answers I have seen throughout the support forum.

    I hope this is clear and you can offer a solution to outputting the schema data as described above.

    Cheers,

    Jason Jeffers

    Tagging along on this one. We just added our first variable products which include various sizes in both litres and grams and have a separate variation for each as such. We also use the Product GTIN and implement a similar function to pass the gtin to rankmath/google. We’ll dive in later today and update with any ideas or solutions.

    Hello,

    To replace AggregateOffer with Offer and to add gtin value, please add following code in your theme’s functions.php file:

    
    add_filter( 'rank_math/snippet/rich_snippet_product_entity', function( $entity ) {
    	if ( ! is_product() ) {
    		return $entity;
    	}
    
    	$product = wc_get_product( get_the_ID() );
    	if ( ! $product->is_type( 'variable' ) ) {
    		$entity['gtin13'] = get_post_meta( get_the_ID(), '_wpm_gtin_code', true );
    		return $entity;
    	}
    
    	$variations = $product->get_available_variations();
    	if ( ! empty( $variations ) ) {
    		$entity['offers'] = [];
    		foreach ( $variations as $variation ) {
    			$price_valid_until = get_post_meta( $variation['variation_id'], '_sale_price_dates_to', true );
    			$entity['offers'][] = [
    				'@type'           => 'Offer',
    				'description'     => strip_tags( $variation['variation_description'] ),
    				'price'           => $variation['display_price'],
    				'priceCurrency'   => get_woocommerce_currency(),
    				'availability'    => $variation['is_in_stock'] ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',
    				'itemCondition'   => 'NewCondition',
    				'priceValidUntil' => $price_valid_until ? date_i18n( 'Y-m-d', $price_valid_until ) : '',
    				'url'             => $product->get_permalink(),
    				'gtin13'          => get_post_meta($variation['variation_id'],'_wpm_gtin_code', true ),
    			];
    		}
    	}
    
    	return $entity;
    } );
    
    

    I hope that helps.

    Hello,

    Since we did not hear back from you for 15 days, we are assuming that you found the solution. We are closing this support ticket.

    If you still need assistance or any other help, please feel free to open a new support ticket, and we will be more than happy to assist.

    Thank you.

Viewing 4 replies - 1 through 4 (of 4 total)

The ticket ‘GTIN numbers for variable products’ is closed to new replies.