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)?
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.