Search code examples
phpwordpresswoocommerceschemahook-woocommerce

Add custom structured data schema to WooCommerce archive pages


On my WooCommerce category pages, I want to grab the existing query results and use it to populate the schema data for "OfferCatalog".

This is the closest function I can find to what I need:

/**
 * Add structured data to the WooCommerce shop page.
 *
 * @param array $markup The structured data markup.
 * @param WP_Query $query The current WP_Query object.
 * @return array The modified structured data markup.
 */
function add_structured_data_to_shop_page( $markup, $query ) {
    if ( is_shop() || is_product_category() || is_product_tag() ) {
        $markup['@type'] = 'CollectionPage';
        $markup['mainEntity']['@type'] = 'OfferCatalog';
        $markup['mainEntity']['itemListElement'] = array();

        $products = $query->get_posts();
        foreach ( $products as $product ) {
            $markup['mainEntity']['itemListElement'][] = array(
                '@type' => 'Product',
                'name' => get_the_title( $product->ID ),
                'url' => get_permalink( $product->ID ),
                'image' => get_the_post_thumbnail_url( $product->ID, 'full' ),
                'description' => get_the_excerpt( $product->ID ),
                'offers' => array(
                    '@type' => 'Offer',
                    'price' => get_post_meta( $product->ID, '_price', true ),
                    'priceCurrency' => get_woocommerce_currency(),
                    'availability' => 'https://schema.org/InStock', // Adjust as needed
                ),
            );
        }
    }

    return $markup;
}
add_filter( 'woocommerce_structured_data', 'add_structured_data_to_shop_page', 10, 2 );

But nothing is output. I suspect the woocommerce_structured_data has been disabled or removed from the category pages. I've tried different hooks, but still see nothing output.

How can I make this work (without repeating the database query)?


Solution

  • Update

    You can not really use existing WooCommerce related filter hooks, as you are adding a new structured data type.

    What you can do is to add your own structured data to the footer like:

    /**
     * Add structured data to the WooCommerce archive pages.
     */
    add_action( 'wp_footer', 'output_wc_archive_pages_structured_data', 10 );
    function output_wc_archive_pages_structured_data() {
        // Only on WooCommerce archives
        if ( is_shop() || is_product_category() || is_product_tag() ) {
            global $wp_query; // Get the current WP_Query object
    
            $markup_data = array(); // Initializing
            
            $markup_data['@context']            = 'https://schema.org/';
            $markup_data['@type']               = 'CollectionPage'; 
            $markup_data['mainEntity']['@type'] = 'OfferCatalog';
    
            $markup_data['mainEntity']['itemListElement'] = array(); // Initializing
            
            $product_posts = $wp_query->get_posts(); // Get the posts from the current query
            
            // Loop through product posts
            foreach ( $product_posts as $product_post ) {
                $product_id = $product_post->ID;
            
                $markup_data['mainEntity']['itemListElement'][] = array(
                    '@type'         => 'Product',
                    'name'          => wp_kses_post( get_the_title( $product_id ) ),
                    'url'           => esc_url( get_permalink( $product_id ) ),
                    'image'         => esc_url( get_the_post_thumbnail_url( $product_id, 'full' ) ),
                    'description'   => wp_strip_all_tags( get_the_excerpt( $product_id ) ),
                    'offers'        => array(
                        '@type'         => 'Offer',
                        'price'         => wc_format_decimal( get_post_meta( $product_id, '_price', true ), wc_get_price_decimals() ),
                        'priceCurrency' => esc_attr( get_woocommerce_currency() ),
                        'availability'  => $markup_data['@context'] . esc_attr( get_post_meta( $product_id, '_stock_status', true ) ),
                    ),
                );
            }
        
            echo '<script type="application/ld+json">' . wc_esc_json( wp_json_encode( $markup_data ), true ) . '</script>';
        }
    }
    

    Code goes in functions.php file of your child theme (or in a plugin). Tested and works.

    enter image description here