I have multiple images in a single product in the post_content area. These are attached to the product but without using the gallery feature.

I want to add structured data to each image attached to the product. I use the following code to extract images to an array $imagess and then apply structured data on each image with a loop.

I want to use multiple Imageobjects and product schema on the same page. This code shows a single image x times of total images.

function wc_add_product_structed_data_images ( $markup ) {
    $imagess = array();
    $imagess = get_children( array( 'post_parent' => get_the_ID(), 'post_type' => 'attachment', 'post_mime_type' => 'image', 'orderby' => 'menu_order', 'order' => 'DESC', 'posts_per_page' => 99 ) );    

     foreach ( $imagess as $attachment ): 
     $markup['@context'] = 'https://schema.org';
     $markup['@type'] = 'ImageObject';
     $markup['contentUrl'] = $attachment->guid;
     $markup['@context'] = 'https://schema.org/';
     $markup['@type'] = 'ImageObject';
     $markup['contentUrl'] = $attachment->guid
     $markup['license'] = 'https://creativecommons.org/licenses/by-nd/2.0/';
     $markup['acquireLicensePage'] = 'https://creativecommons.org/licenses/by-nd/2.0/';
     $markup['creditText'] = 'Creative Department';
     $markup['copyrightNotice'] = '© 2008 to 2022';
     $markup['@context'] = 'https://schema.org/';
     $markup['ImageObject']['@type'] = 'CreativeWork';
     $markup['ImageObject']['creator'] = 'abc';
     $markup['embeddedTextCaption'] = 'emb text caption';
return $markup;
add_filter( 'woocommerce_structured_data_context', 'wc_add_product_structed_data_images', 10, 2 );

1 Answer 1


What I understand from your question, you’ve already generated product schema via Woocommerce and you want to append/insert additional multiple ImageObject - for ALL the images attached (added within the content/product long description/etc./no-gallery images) to the product - within generated product schema.

Before heading to the solution, these are the issues with your code:

  • foreach clause is not closed
  • creator object type can be Organization or Person only which both belongs to CreativeWork category. Use the object type i-e: Organization or Person, not the CreativeWork category name here since it’ll fail rich results test.
  • In contentUrl, you are using only guid which is not an URL. guid is only a unique identifier, however, markup needs full URL to the attachment.
  • Use ACTUAL context in the filter hook name i-e: product. So instead of woocommerce_structured_data_context use woocommerce_structured_data_product . If you leave generic word context, it will override markup for all context like breadcrumb and others. Keep your context specifically product for product related images.
  • In schema, you don't need to enforce orderby and order, so clean them from get_children arguments
  • Since you want ImageObject markup for ALL attachments, clean out the posts_per_page too from arguments.

Solution (Tested, Validated, & Vetted for Recommended Usage)

The solution is tested with latest (up to the date) Woocommerce, Wordpress, and also validated with Google Rich Results testing tool.

add_filter( 'woocommerce_structured_data_product', 'wc_add_product_structed_data_images', 10, 1 );
function wc_add_product_structed_data_images ( $markup ) {
    $images = array(); //replaced ss with s
    $images = get_children( 
        array( 'post_parent' => get_the_ID(), 'post_type' => 'attachment', 'post_mime_type' => 'image') 

    if(count($images) > 0) { // just ensuring we really have childrens or additional images
        /** prepare image objects before insertion */
        $imageObjects = array();
        foreach($images as $i) {
            //Prepare the single image meta data
            $singleImageObject = array(
                "@type" => "ImageObject",
                "name" => "Name of the image",         
                "creator" => array(
                    "@type" => "Organization",
                    "name" => "Name of Creator"
                "creditText" => "Creative Dept.",
                "embeddedTextCaption" => "The caption of image.",
                "description" => "The short description of the image.",
                "contentUrl" => wp_get_attachment_url($i->ID),
                "license" => "https://creativecommons.org/licenses/by-nd/2.0/",
                "acquireLicensePage" => "https://creativecommons.org/licenses/by-nd/2.0/",
                "copyrightNotice" => "2008 to 2022"
            /** Pushing image to image objects */ 
            $imageObjects[] = $singleImageObject; 
        /** Based on image property existence, lets push the objects in markup now */
        if(isset($markup['image'])) { //if it is set, it returns an URL string only; not ImageObject. 
                $markup['image'] = array($markup['image']); //converting to array while preserving first image added by woo
                $markup['image'][] = $imageObjects;
        } else { //if no image property is yet set, lets set it and add ImageObject array
            $markup['image'] = array(); 
            $markup['image'][] = $imageObjects;

        return $markup;

    return $markup;

Code Explanation

  • I changed the filter hook from context to product and cleaned the arguments.
  • Firstly: If we really have attachments, I prepared the ImageObject markup by looping through all images, creating an ImageObject associate array $singleImageObject for individual image, and then pushing/merging all of the ImageObject into one array $imageObjects.
  • Secondly: The image property might already be set by Woo for featured image and if it sets it in string format. It means, we must either append the image property or we should override it completing by adding featured image and additional ones. In the above code, I checked if we've the image property set, I converted the data type to array (since if it set already it is string), then I pushed back the existing string URL for featured image to preserve it, and finally pushed/appended the prepared $imageObjects into the $markup['image']

Additional Notes

  • In the ImageObject markup, you can replace image name, creator name and type (Organization or Person) only, embeddedTextCaption with image caption, and other properties to their respective dynamic or static values or as needed.
  • If you're developing a theme with it and using this filter, make sure your theme uses wp_footer() in the footer since this filter uses wp_footer hook to output its contents.
  • Thank you so much. It worked ... Commented Dec 10, 2022 at 9:35
  • 1
    Your welcome. :)
    – Aqsa J.
    Commented Dec 11, 2022 at 1:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.