Set WooCommerce product variation thumbnail by attribute

WooCommerce allows you to assign a thumbnail to each of your product variations. If you have a lot of variations, this will be very time-consuming. In some cases, it would be more convenient to assign a thumbnail to each attribute term.

If you’re selling T-shirts for example, you would have 2 attributes: color and size. Each color would need a unique image, no matter what size it is.

There are 2 big steps in this guide:

  1. Admin: Add thumbnail fields for each term (red, green, blue, …) in a certain attribute (color)
  2. Frontend: Replace the default thumbnail by this thumbnail whenever a variation is displayed on the product page

1. Admin: Add thumbnail fields

Fig. 1: Fields for each attribute term

1.1 Add a custom product data tab

add_filter( 'woocommerce_product_data_tabs', function( $tabs ) {

	$tabs['variation_images'] = array(
		'label'    => __( 'Variation images', 'woocommerce' ),
		'target'   => 'variation_images',
		'class'    => array( 'show_if_variable' ),
		'priority' => 65,
	);

	return $tabs;
	
} );

1.2 Add fields to this tab

<?php
add_action( 'woocommerce_product_data_panels', function(){
	global $post, $thepostid, $product_object;
	?>
	<div id="variation_images" class="panel woocommerce_options_panel hidden">
		<div class="options_group show_if_variable">
			<div class="options_group">
				<?php if( $product_object->is_type( 'variable' ) ): ?>
					<?php $product_variation_attributes = $product_object->get_variation_attributes(); ?>
					<?php if ( $product_object->is_type( 'variable' ) && $product_variation_attributes ) : ?>
						<?php foreach ( $product_variation_attributes as $attribute_name => $product_terms ) : ?>
							<table class="widefat striped table-attribute-thumbnails">
								<thead>
									<tr>
										<th colspan="2"><strong><?php echo wc_attribute_label( $attribute_name ); ?></strong></th>
									</tr>
								</thead>
								<tbody>
									<?php foreach ( $product_terms as $product_term ) : ?>
										<tr>
											<th>
												<?php echo esc_html( ucfirst( $product_term ) ); ?>
											</th>
											<td>
												<?php
												$image_id = get_post_meta( $thepostid, 'variation_image_' . $attribute_name . '_' . $product_term, true );
												$thumbnail = wp_get_attachment_image( $image_id, 'thumbnail' );
												if ( $thumbnail ) {
													printf( '<a href="#" class="btn-media-upload">%s</a><br /><a href="#" class="btn-media-remove">%s</a>', $thumbnail, __( 'Remove image', 'wc-variation-general-thumbnail' ) );
												} else {
													printf( '<a href="#" class="btn-media-upload">Upload image</a><a href="#" class="btn-media-remove" style="display:none">%s</a>', __( 'Remove image', 'wc-variation-general-thumbnail' ) );
												}
												?>
												<input type="hidden" name="variation_images[<?php echo esc_html( $attribute_name ); ?>][<?php echo esc_html( $product_term ); ?>]" value="<?php echo esc_html( get_post_meta( $thepostid, 'variation_image_' . $attribute_name . '_' . $product_term, true ) ); ?>">
											</td>
										</tr>
									<?php endforeach; ?>
								</tbody>
							</table>
						<?php endforeach; ?>
						<?php wp_nonce_field( 'variation_images_' . $product_object->get_ID(), 'save_variation_images' ); ?>
					<?php endif; ?>
				<?php endif; ?>
			</div>
		</div>
	</div>
	<?php
} );
?>

1.3 Save fields

add_action( 'woocommerce_process_product_meta_variable', function( $post_id ) {
	if ( isset( $_POST['variation_images'] ) && check_admin_referer( 'variation_images_' . $post_id, 'save_variation_images' ) ) {
		$attributes = wp_unslash( $_POST['variation_images'] );
		foreach ( $attributes as $attribute => $terms ) {
			foreach ( $terms as $term => $image ) {
				if ( $image ) {
					update_post_meta( $post_id, 'variation_image_' . $attribute . '_' . $term, filter_var( $image, FILTER_SANITIZE_STRING ) );
				} else {
					delete_post_meta( $post_id, 'variation_image_' . $attribute . '_' . $term );
				}
			}
		}
	}
} );

1.4 Some JavaScript to enable the WordPress media library

This code was based on Misha Rudrastyh’s blog post: https://rudrastyh.com/wordpress/customizable-media-uploader.html

jQuery(document).ready(function($){
	// on upload button click
	$('body').on( 'click', '.btn-media-upload', function(e){
 
		e.preventDefault();
 
		var button = $(this),
		custom_uploader = wp.media({
			title: 'Insert image',
			library : {
				// uploadedTo : wp.media.view.settings.post.id, // attach to the current post?
				type : 'image'
			},
			button: {
				text: 'Use this image' // button label text
			},
			multiple: false
		}).on('select', function() { // it also has "open" and "close" events
			var attachment = custom_uploader.state().get('selection').first().toJSON();
			button.html('<img src="' + attachment.sizes.thumbnail.url + '">').next().val(attachment.id).next().show();
			button.parent().find('input').val(attachment.id)
			button.parent().find('.btn-media-remove').show();
		}).open();
 
	});
 
	// on remove button click
	$('body').on('click', '.btn-media-remove', function(e){
 
		e.preventDefault();
 
		var button = $(this);
		button.parent().find('input').val('')
		button.hide()
		button.parent().find('.btn-media-upload').html('Upload image');
	});
});

1.5 Style the table

.table-attribute-thumbnails {
  border: 0 !important;
}

.table-attribute-thumbnails thead {
  background-color: #007cba;
}

.table-attribute-thumbnails thead tr th {
  color: white;
}

2. Frontend: Replace the thumbnail

WooCommerce allows us to customize a variation before it is displayed with the woocommerce_available_variation filter. First we need to check if an image is explicitly defined for this variation, if so, display this image instead of our global attribute term thumbnail.

After this, we iterate each of this product’s attribute terms and see if we defined a thumbnail. If so, we replace the default thumbnail by the term thumbnail.

add_filter( 'woocommerce_available_variation', function( $attr, $product, $variation ) {

	// Get the variation image.
	$default_image = get_post_meta( $variation->get_ID(), '_thumbnail_id', true );

	// Only find the attribute image if no default image is set.
	if ( ! $default_image ) {
		$attributes = $variation->get_variation_attributes();
		foreach ( $attributes as $attribute_name => $value ) {
			$stripped_attribute = str_replace( 'attribute_', '', $attribute_name );
			$attribute_image = get_post_meta( $product->get_ID(), 'variation_image_' . $stripped_attribute . '_' . $value, true );
			if ( $attribute_image ) {
				$attr['image'] = wc_get_product_attachment_props( $attribute_image );
				break;
			}
		}
	}

	return $attr;

}, 10, 3 );

3. Caveats

  • The product needs to be saved before these thumbnail fields are displayed in the admin. So you need to add your attributes & terms, save your product. The page gets refreshed and will display the thumbnail fields.
  • If you set thumbnails for multiple attributes, e.g. size & color, only the first one will show.

4. Plugin

This code is available as a WooCommerce Extension on Github: https://github.com/tombroucke/otomaties-woocommerce-variation-general-thumbnail

10 Replies to “Set WooCommerce product variation thumbnail by attribute”

  1. Thanks for this plugin! I’m looking for it but after few test it looks like it works not in every sigle case.

    It works only when I set each of attribute separately, for example – redd and whitee color:
    https://i.imgur.com/gsqbYwg.jpg
    It doesnt work when I want to set variations in group (“any color”)
    https://i.imgur.com/XH3qy3Q.jpg
    In both cases I’ve set different images on diferrent color attributes, but it works only in first case, in second case no image is loaded…
    When I have a lot of attributes and I have to make a lot of variations I will not make more variation because of color…
    Is this possible to make modification in this plugin to works in second case too?

    1. Hi Matt,
      De plugin werkt zeker hoor, ik gebruik deze op enkele productiewebsites zonder problemen. Zoals Tomek hieronder al zei, moet je wel effectief variaties aanmaken voor alle eigenschappen. Wanneer je kiest voor “Any color” bijvoorbeeld, zal dit niet werken.

      1. Is there possibility to modify this plugin to work with “any color”? How much will cost this modification?

  2. Hoi Tom,

    Zeer handige plugin. Scheelt idd een hoop tijd.
    Is de code makkelijk aan te passen zodat ik bv 1 image aan “Dames”, “Grijs” en dan alle “size” kan toevoegen?
    Dit zou me enorm helpen. Dankje

    1. Dag Bas,

      De huidige code kan dit inderdaad niet. Ik heb momenteel geen tijd om een oplossing te zoeken voor deze use-case. Als je zelf bedreven bent in PHP, mag je zeker een oplossing zoeken en een PR openen op github.

      Mvg,
      Tom

  3. Thank you so much, you’re life saver, glad i found this. Will you upload this to wordpress.org? That will help save alot of manual work.

  4. Thank you for this Tom. I have been breaking my head over this work for weeks.
    I have been able to see the uploaded images in the backend, however, I cannot see those in the frontend yet.

    Probably because, the only thing I have done is downloaded the plugin and uploaded it to my site,

    As a layman, I am not sure how to do the rest

    a) Added repository to your composer.json like this > https://prnt.sc/huZtuskqr7ZG
    but it shows a syntax error

    b) I also do not know how to “run” composer require tombroucke/otomaties-woocommerce-variation-general-thumbnail : https://prnt.sc/qe0vlKysVFtG
    I am using a namecheap shared hosting

    Appreciate any help any one can send my way.

    Thanks much!

    1. Hi Emmanuel,

      If you’re seeing the uploaded images in the backend, the plugin is correctly installed. The “Bedrock” section in the readme file is intended for people who use roots/bedrock.
      It might be possible that your theme lacks support for variation images. You could try to switch to a default theme (like storefront), and see if the images change when selecting a different variation.

      Kind regards,
      Tom

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *

Overtuigd?

Neem dan contact op voor een offerte of vrijblijvend gesprek.
Stuur een e-mail naar [email protected] of maak gebruik van het contactformulier.